英文:
Golang: How do I use fmt.Fscanf() & fmt.Sscanf() correctly?
问题
我正在处理一个程序,该程序应该读取文件的内容并从中提取数据。我尝试使用fmt.Fscanf()
逐行扫描内容,但出于某种原因,我无法正确地使其工作。整行内容只被扫描到第一个字符串参数中。我在这里做错了什么?
我猜测它不起作用是因为值之间没有用空格分隔,但我找不到解决方案。感谢您的帮助和专业知识!
func main() {
file, err := os.Open("./CNMSshvol20220105.txt")
if err != nil{
log.Fatal(err)
}
defer file.Close()
var m map[string][]int
m = make(map[string][]int)
for{
row := ""
date := ""
symbol := ""
shortVolume := 0
shortExemptVolume := 0
totalVolume := 0
markets := ""
//Date|Symbol|ShortVolume|ShortExemptVolume|TotalVolume|Market
var n int
n, err := fmt.Fscanf(file, "%s\n", &row)
fmt.Sscanf(row, "%s|%s|%d|%d|%d|%s", &date, &symbol, &shortVolume, &shortExemptVolume, &totalVolume,&markets)
fmt.Println(date)
if n == 0 || err != nil{
log.Fatalf("Fscanf: %v\n", err)
break
}
fmt.Println(symbol, shortVolume, shortExemptVolume, totalVolume)
m[strings.ToLower(symbol)] = append(m[strings.ToLower(symbol)],shortVolume)
m[strings.ToLower(symbol)] = append(m[strings.ToLower(symbol)],shortExemptVolume)
m[strings.ToLower(symbol)] = append(m[strings.ToLower(symbol)],totalVolume)
}
//Retrieve values if the key exists
for{
fmt.Println("\n\n\n\nEnter ticker to retrieve short volume information.")
var s string
fmt.Scanf("%s",&s)
data, ok := m[strings.ToLower(s)]
if !ok{
fmt.Println("Not Found.")
continue
}
for _, value := range data{
fmt.Println(value)
}
}
}
请注意,我只翻译了代码部分,其他内容不包括在内。
英文:
I'm working on a program that is supposed to read the contents of a file and extract the data from it. I've tried to use fmt.Fscanf() to scan the contents line by line, but for some reason, I can't get it to work correctly. The entire line gets scanned into the first string argument only. What am I doing wrong here?
I'm assuming it's not working because the values aren't separated by whitespaces but I couldn't find a solution. Thank you for your help and expertise!
20220105|AA|1051302|4323|3132468|B,Q,N
20220105|AAA|61|0|62|Q
20220105|AAAU|19404|0|57137|Q,N
20220105|AAC|35524|0|39861|Q,N
20220105|AAC/WS|1180|0|2000|N
20220105|AACG|1805439|32577|3484265|B,Q,N
func main() {
file, err := os.Open("./CNMSshvol20220105.txt")
if err != nil{
log.Fatal(err)
}
defer file.Close()
var m map[string][]int
m = make(map[string][]int)
for{
row := ""
date := ""
symbol := ""
shortVolume := 0
shortExemptVolume := 0
totalVolume := 0
markets := ""
//Date|Symbol|ShortVolume|ShortExemptVolume|TotalVolume|Market
var n int
n, err := fmt.Fscanf(file, "%s\n", &row)
fmt.Sscanf(row, "%s|%s|%d|%d|%d|%s", &date, &symbol, &shortVolume, &shortExemptVolume, &totalVolume,&markets)
fmt.Println(date)
if n == 0 || err != nil{
log.Fatalf("Fscanf: %v\n", err)
break
}
fmt.Println(symbol, shortVolume, shortExemptVolume, totalVolume)
m[strings.ToLower(symbol)] = append(m[strings.ToLower(symbol)],shortVolume)
m[strings.ToLower(symbol)] = append(m[strings.ToLower(symbol)],shortExemptVolume)
m[strings.ToLower(symbol)] = append(m[strings.ToLower(symbol)],totalVolume)
}
//Retrieve values if the key exists
for{
fmt.Println("\n\n\n\nEnter ticker to retrieve short volume information.")
var s string
fmt.Scanf("%s",&s)
data, ok := m[strings.ToLower(s)]
if !ok{
fmt.Println("Not Found.")
continue
}
for _, value := range data{
fmt.Println(value)
}
}
}```
</details>
# 答案1
**得分**: 3
[`fmt.Sscanf` 文档](https://pkg.go.dev/fmt#Sscanf) 中写道:_Sscanf 函数会扫描参数字符串,并根据格式将连续的**以空格分隔**的值存储到连续的参数中_
`|` 字符不是空格,并且每个格式占位符都是独立的,所以 `%s|` 并不意味着匹配到第一个 `|` 之前的任意字符串,而是匹配到不包含空格的任意字符串,然后再匹配一个 `|`。
你可以逐行处理文件,例如使用 [`strings.Split()`](https://pkg.go.dev/strings#Split) 函数按 `|` 分割每一行,然后根据需要将值解析到变量中:
``` go
row := "20220105|AACG|1805439|32577|3484265|B,Q,N"
tokens := strings.Split(row, "|")
if len(tokens) != 6 {
log.Fatal("文件中的行格式错误...")
}
// 进行适当的转换...
date, err := strconv.Atoi(tokens[0])
if err != nil {
log.Fatal("第一个标记不是整数")
}
symbol := tokens[1]
// ...
fmt.Println(date, symbol)
或者如 Volker 在他的评论中指出的,你可以使用 encoding/csv
包,并将其 Reader
的 Comma
(分隔符)选项设置为 |
:
f := `20220105|AA|1051302|4323|3132468|B,Q,N
20220105|AAA|61|0|62|Q`
r := csv.NewReader(strings.NewReader(f))
r.Comma = '|'
data, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}
// 处理数据,进行适当的转换
fmt.Print(data)
英文:
fmt.Sscanf
documentation says: Sscanf scans the argument string, storing successive space-separated values into successive arguments as determined by the format
|
character is not space and each format placeholder is independent of any other, so %s|
does not mean match any string up to a first |
but instead match any string not including space, and then a |
.
You can process file line by line, split each line e.g using strings.Split()
by |
and then parse values into variables as you need:
row := "20220105|AACG|1805439|32577|3484265|B,Q,N"
tokens := strings.Split(row, "|")
if len(tokens) != 6 {
log.Fatal("Bad line in file...")
}
// Do appropriate conversions...
date, err := strconv.Atoi(tokens[0])
if err != nil {
log.Fatal("first token is not an integer")
}
symbol := tokens[1]
// ...
fmt.Println(date, symbol)
or as Volker pointed out in his comment you can use package encoding/csv
and set its Reader
Comma
(separator) option to |
:
f := `20220105|AA|1051302|4323|3132468|B,Q,N
20220105|AAA|61|0|62|Q`
r := csv.NewReader(strings.NewReader(f))
r.Comma = '|'
data, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}
// Process data, do appropriate conversions
fmt.Print(data)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论