哦, 这该死的txt回车符~
- 今天的golang学习被txt中的回车符困扰了半个小时, 最后才傻傻的发现, mark一下
问题描述
我想从两个文件 test1.txt
和test2.txt
中分别按行读取字符串, 然后统计字符串的出现次数.
两文件内容分别如下, 是我手动键入的:
test1
test2
test2
test1
- 按照逻辑来说, 最终统计结果是,
test1
和test2
都分别出现两次, - 但是结果却并不如我所想, map中有四个字符串, 打印出来是
test1
test1
test2
test2
明明它们都一样, 怎么统计的呀, 难道是golang的map有问题???
- 遇事先想自己的问题, 不能怀疑语言, 不能怀疑编译器, 这是从百度面试官那里学到的
我的代码如下
package mainimport ("fmt""io/ioutil""os""strings"
)func main() {//fmt.Println("dup3,test filename")counts := make(map[string]int)filenames := []string{"data\\test1.txt","data\\test2.txt"}for _,fileName := range filenames{data, err := ioutil.ReadFile(fileName) // 返回字节数组, 以及错误类型//fmt.Println(data)if err != nil{fmt.Fprintf(os.Stderr, "dup3: %v\n", err)continue}for _,str := range strings.Split(string(data),"\n"){counts[str]++}}for line,cnt := range counts{if cnt >1 {fmt.Printf("%d\t%s\n",cnt,line)}}
}
问题分析与解决
- 为什么
test1
和test1
不同呢, 它们明明是同一个字符串 - 经过思考, 我猜到了答案, 回车符, 然后就是验证
- 原因在于, readFile是读入的字节切片, 而不是按行读入
- 我们将readFile读取到的字节切片直接打印, 可以得到如下结果
[116 101 115 116 49 13 10 116 101 115 116 50]
[116 101 115 116 50 13 10 116 101 115 116 49]
- 这是因为我在敲txt的时候, 习惯性敲了回车键, ascll码13对应的就是
\r
, 即回车, 而10对应的就是换行 - 所以原因是因为我手敲的文件内容, 其中为了换行, 用了回车符, 而换行本身又是
\n
, 所以在上面我们用/n
来进行split
会产生长度为6和长度为5的字符串, 分别是test1\r
和test1
,test2\r
和test2
, 自然又不一样了, 然后打印出来, 因为经过了系统函数的格式化, 删除了回车符, 所以看起来都一样, 于是搞不清是哪出了问题 - 这个问题只是因为我们手敲导致, 如果是代码写入txt, 那么一般都不会使用回车符+换行符, 而是直接使用换行符, 则没有本文的问题出现
- 解决方法为, 将
split
的sep
换成\r\n
即可 - 然后就能正确打印结果啦
哦, 这该死的txt回车符~
- 今天的golang学习被txt中的回车符困扰了半个小时, 最后才傻傻的发现, mark一下
问题描述
我想从两个文件 test1.txt
和test2.txt
中分别按行读取字符串, 然后统计字符串的出现次数.
两文件内容分别如下, 是我手动键入的:
test1
test2
test2
test1
- 按照逻辑来说, 最终统计结果是,
test1
和test2
都分别出现两次, - 但是结果却并不如我所想, map中有四个字符串, 打印出来是
test1
test1
test2
test2
明明它们都一样, 怎么统计的呀, 难道是golang的map有问题???
- 遇事先想自己的问题, 不能怀疑语言, 不能怀疑编译器, 这是从百度面试官那里学到的
我的代码如下
package mainimport ("fmt""io/ioutil""os""strings"
)func main() {//fmt.Println("dup3,test filename")counts := make(map[string]int)filenames := []string{"data\\test1.txt","data\\test2.txt"}for _,fileName := range filenames{data, err := ioutil.ReadFile(fileName) // 返回字节数组, 以及错误类型//fmt.Println(data)if err != nil{fmt.Fprintf(os.Stderr, "dup3: %v\n", err)continue}for _,str := range strings.Split(string(data),"\n"){counts[str]++}}for line,cnt := range counts{if cnt >1 {fmt.Printf("%d\t%s\n",cnt,line)}}
}
问题分析与解决
- 为什么
test1
和test1
不同呢, 它们明明是同一个字符串 - 经过思考, 我猜到了答案, 回车符, 然后就是验证
- 原因在于, readFile是读入的字节切片, 而不是按行读入
- 我们将readFile读取到的字节切片直接打印, 可以得到如下结果
[116 101 115 116 49 13 10 116 101 115 116 50]
[116 101 115 116 50 13 10 116 101 115 116 49]
- 这是因为我在敲txt的时候, 习惯性敲了回车键, ascll码13对应的就是
\r
, 即回车, 而10对应的就是换行 - 所以原因是因为我手敲的文件内容, 其中为了换行, 用了回车符, 而换行本身又是
\n
, 所以在上面我们用/n
来进行split
会产生长度为6和长度为5的字符串, 分别是test1\r
和test1
,test2\r
和test2
, 自然又不一样了, 然后打印出来, 因为经过了系统函数的格式化, 删除了回车符, 所以看起来都一样, 于是搞不清是哪出了问题 - 这个问题只是因为我们手敲导致, 如果是代码写入txt, 那么一般都不会使用回车符+换行符, 而是直接使用换行符, 则没有本文的问题出现
- 解决方法为, 将
split
的sep
换成\r\n
即可 - 然后就能正确打印结果啦