什么是模仿C中存在的否定扫描集的方式?相当于C的否定扫描集
有关示例输入字符串:aaaa, bbbb
在去使用:
fmt.Sscanf(input, "%s, %s", &str1, &str2)
结果仅str1
被设置为:aaaa,
在C人们可以使用一个格式字符串作为"%[^,], %s"
为了避免这个问题,有没有办法在去做到这一点?
什么是模仿C中存在的否定扫描集的方式?相当于C的否定扫描集
有关示例输入字符串:aaaa, bbbb
在去使用:
fmt.Sscanf(input, "%s, %s", &str1, &str2)
结果仅str1
被设置为:aaaa,
在C人们可以使用一个格式字符串作为"%[^,], %s"
为了避免这个问题,有没有办法在去做到这一点?
Go不支持这种直接像C,部分原因是因为你应该读一本线,并使用类似strings.FieldsFunc
。但这自然是一个非常简单的观点。对于以均匀格式进行格式化的数据,您可以使用bufio.Scanner
基本上对任何io.Reader
执行相同操作。然而,如果你不得不处理类似这样的格式:
// Name; [email protected]
//
// Anything other than ';' is valid for name.
// Anything before '@' is valid for email.
// For domain, only A-Z, a-z, and 0-9, as well as '-' and '.' are valid.
sscanf("%[^;]; %[^@]@%[-." ALNUM "]", name, email, domain);
那么你会遇到麻烦,因为你现在正在处理一个特定的状态。在这种情况下,您可能更喜欢使用bufio.Reader
手动解析事情。还可以选择执行fmt.Scanner
。下面是一些示例代码给大家的是多么容易实现fmt.Scanner
一个想法:
// Scanset acts as a filter when scanning strings.
// The zero value of a Scanset will discard all non-whitespace characters.
type Scanset struct {
ps *string
delimFunc func(rune) bool
}
// Create a new Scanset to filter delimiter characters.
// Once f(delimChar) returns false, scanning will end.
// If s is nil, characters for which f(delimChar) returns true are discarded.
// If f is nil, !unicode.IsSpace(delimChar) is used
// (i.e. read until unicode.IsSpace(delimChar) returns true).
func NewScanset(s *string, f func(r rune) bool) *Scanset {
return &Scanset{
ps: s,
delimFunc: f,
}
}
// Scan implements the fmt.Scanner interface for the Scanset type.
func (s *Scanset) Scan(state fmt.ScanState, verb rune) error {
if verb != 'v' && verb != 's' {
return errors.New("scansets only work with %v and %s verbs")
}
tok, err := state.Token(false, s.delimFunc)
if err != nil {
return err
}
if s.ps != nil {
*s.ps = string(tok)
}
return nil
}
这不是C的scansets,但它是足够接近。如前所述,无论如何,即使使用格式化输入,您也应该验证您的数据,因为格式化缺乏上下文(并且在处理格式时添加它会违反KISS原则并且恶化代码的可读性)。
例如,像这样的短正则表达式不足以验证域名,简单的扫描集简直就等于[A-Za-z0-9.-]
。但是,扫描集足以从文件或其他任何可能使用的读取器中扫描字符串,但仅仅验证字符串是不够的。为此,一个正则表达式或甚至一个合适的库将是一个更好的选择。
你总是可以用正则表达式;
re := regexp.MustCompile(`(\w+), (\w+)`)
input := "aaaa, bbbb"
fmt.Printf("%#v\n", re.FindStringSubmatch(input))
// Prints []string{"aaaa, bbbb", "aaaa", "bbbb"}