Golang:我如何正确使用 fmt.Fscanf() 和 fmt.Sscanf() 函数?

huangapple go评论126阅读模式
英文:

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 包,并将其 ReaderComma(分隔符)选项设置为 |

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 := &quot;20220105|AACG|1805439|32577|3484265|B,Q,N&quot;
tokens := strings.Split(row, &quot;|&quot;)
if len(tokens) != 6 {
	log.Fatal(&quot;Bad line in file...&quot;)
}

// Do appropriate conversions...
date, err := strconv.Atoi(tokens[0])
if err != nil {
	log.Fatal(&quot;first token is not an integer&quot;)
}
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 = &#39;|&#39;
data, err := r.ReadAll()
if err != nil {
	log.Fatal(err)
}

// Process data, do appropriate conversions

fmt.Print(data)

huangapple
  • 本文由 发表于 2022年1月7日 12:58:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/70616780.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定