fmt包中跟scan相关的函数总共有9个,最基础的Fscanf函数
Fscanf函数的声明是
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
作用:Fscanf从r(即实现io.Reader接口的类型)扫描文本,根据format 参数指定的格式将成功读取的空白分隔的值保存进成功传递给本函数的参数(注意 换行也是认为空白分割的)。返回成功扫描的条目个数和遇到的任何错误。
例子:
var(
isOk bool
str string
)
len,err :=fmt.Fscanf(os.Stdin,"%t %s",&isOk,&str)
if err != nil{
fmt.Println(err)
}
fmt.Println("读取到的长度",len) //读取到的长度2
//假设终端输入 t string\n
fmt.Println(isOk,str) //true string
在Fscanf函数上延伸的函数有
Scanf 与Fscanf的区别是从标准输入扫描文本,实现过程如下:
func Scanf(format string, a ...interface{}) (n int, err error) {
return Fscanf(os.Stdin, format, a...)
}
Sscanf 与Fscanf的区别是从字符串中扫面文本,实现过程如下:
type stringReader string
//实现io.Reader接口的方法
func (r *stringReader) Read(b []byte) (n int, err error) {
n = copy(b, *r)
*r = (*r)[n:]
if n == 0 {
err = io.EOF
}
return
}
//具体调用
func Sscanf(str string, format string, a ...interface{}) (n int, err error) {
return Fscanf((*stringReader)(&str), format, a...)
}
剩下三个 scan,Fscan,SScan与前面f后缀函数的区分是format格式类型不需要指定,系统会默认使用v格式(默认类型)
Fscanln,SScanln,scan与不含ln的函数区别是换行不是当着空白分割而是当做结束符
var(
is_ok bool
st string
)
_, err := fmt.Sscan("t\n string", &is_ok, &st)
if err != nil {
fmt.Println("错误:", err)
}
fmt.Println(is_ok, st) //输出 true string
//
_, err := fmt.Sscanln("t\n string", &is_ok, &st)
if err != nil {
fmt.Println("错误:", err)
}
fmt.Println(is_ok, st)
//输出
// 错误: unexpetced newline
// true
可以看到在Sscanln函数中碰到'\n'后面的字符串就不在解析了,即将\n当做了结束符,导致st变量需要的字符串获取不到,报出了
unexpected newline的错误
具体在GO源码中的流程是通过SkipSpace()函数处理的,该函数在每次扫描一个参数给变量前调用,用来先跳过一段空白字符,在这里会决定\n是空白字符 还是结束符
func (s *ss) SkipSpace() {
for {
//扫描一个字符
r := s.getRune()
//如果是结束符退出函数
if r == eof {
return
}
//s.peek("\n")是表示 判断当前读取到的字符的下一个字符是否是\n
//这段意思是 如果当前字符 跟下个字符 拼成\r\n 就继续向前扫描
if r == '\r' && s.peek("\n") {
continue
}
//我们主要看的是这里,碰到一个\n时,是怎么处理的
if r == '\n' {
//这里可以看到如果s.nlIsSpace是ture时就当做空白字符
//有后缀ln的函数,就是将该变量设置成了false
//所以就抛出了下面的错误
if s.nlIsSpace {
continue
}
s.errorString("unexpected newline")
return
}
if !isSpace(r) {
s.UnreadRune()
break
}
}
}