Java转go算法之Scanner使用一篇看懂

文章摘要:go语言中使用scanner去标准输入中扫描数据的过程以及原理

1、Scanner的简单使用

会默认以换行符作为结束标准

//创建一个新的Scanner,它将从标准输入读取数据
scanner := bufio.NewScanner(os.Stdin)

for scanner.Scan() {
    fmt.Println("输入的文本是:", scanner.Text())
}

2、Scanner读取文件

func main() {
	file, err := os.Open("example.txt")
	if err != nil {
		fmt.Println("打开文件失败:", err)
		return
	}
	defer file.Close()

	//bufio.NewScanner中传参io.Reader 接口的类型,为了可以从输入流中读取数据的
	scanner := bufio.NewScanner(file)
	//读取到的数据会以字节切片([]byte)的形式存储在内部缓冲区s.buf中。
	//之后可以通过scanner.Bytes()方法获取缓冲区中的数据。或者Text将其转换为字符串格式
	for scanner.Scan() {
		fmt.Println(scanner.Text()) // 打印每一行
	}
}

3、Scanner通过io.Reader读取数据的过程

用到的方法就是scanner.Scan()

1、详细看Scan()内部

首先看Scan是一个方法,调用它的接口类型是Scanner,下面是这个对象的三个变量

  • r:是io.Reader接口类型的对象,代表输入数据的源头。可以是文件、网络连接、标准输入等任何实现了 io.Reader 接口的数据源
  • split:是 一个可选的分割函数,用于定义如何从输入数据中提取 token(分割的标识)
  • maxTokenSize:是指定了单个 token 的最大长度,默认是64k,超过会报错!
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        ScanLines,
		maxTokenSize: MaxScanTokenSize,
	}
}

从输入源(io.Reader)中读取到的数据会以字节的形式存储到内部缓冲区s.buf,之后就可以根据split,找到数据中的token(分割的标识)进行分割。

2、详细看scan扫描分割符(token)原理

比如说 “世 界” 这个字符串,采用让他用空格作为分隔符将两个字打印 世界转换为字节码就是[228 184 150 32 231 149 140],一个汉字三个字节,最中间的32,就是空格符的Ascall码,当scan()在扫描 内部缓冲区s.buf 扫到32,然后就进行分割

func main() {
	// 将字符串转换成字节数组
	str := "世 界"
	byteArr := []byte(str)
	// 输出字节数组
	fmt.Println(byteArr) //[228 184 150 32 231 149 140]

	// 将字符串转换为 io.Reader 类型
	reader := bytes.NewReader([]byte(str))

	// 创建 Scanner 对象并指定分割函数为空格
	scanner := bufio.NewScanner(reader)
	scanner.Split(bufio.ScanWords)//bufio.ScanWords指的就是token为按照单词为单位进行分割

	// 遍历 Scanner 输出每个 token
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
	//世
	//界
}
  • 空格(space) ASCII 码值: 32
  • 制表符(Tab) ASCII 码值: 9
  • 换行符(Newline) ASCII 码值: 10 (LF, Line Feed)
  • 回车符(carriage Return) ASCII 码值: 13 (CR)
  • EOF(End of File) ASCII 码值: 4

当然记这些ASCLL码符号还不够麻烦呢,Go语言有内置的几个预定义函数,可以实现简单的分割类型

3、详细看scanner内置的Split分割函数使用

  1. bufio.ScanBytes
    • 按照字节为单位进行分割。

    • 每次返回一个字节的 []byte

    • func main() {
      	input := "Hello, world!"
      	scanner := bufio.NewScanner(strings.NewReader(input))
      	scanner.Split(bufio.ScanBytes)
      
      	for scanner.Scan() {
      		fmt.Printf("%q ", scanner.Bytes())
      	}
      	if err := scanner.Err(); err != nil {
      		fmt.Println("Error:", err)
      	}
      }
      //"H" "e" "l" "l" "o" "," " " "w" "o" "r" "l" "d" "!"
      
  2. bufio.ScanRunes
    • 按照 Unicode 字符为单位进行分割。

    • 每次返回一个 Unicode 字符的 []rune

    • func main() {
      	input := "世界"
      	scanner := bufio.NewScanner(strings.NewReader(input))
      	scanner.Split(bufio.ScanRunes)
      
      	for scanner.Scan() {
      		fmt.Printf("%q ", scanner.Text())
      	}
      	if err := scanner.Err(); err != nil {
      		fmt.Println("Error:", err)
      	}
      }//"世" "界"
      
  3. bufio.ScanLines
    • 按照行为单位进行分割。

    • 每次返回一行数据,不包括行尾的换行符。

    • func main() {
      	input := "Hello,\nworld!\n"
      	scanner := bufio.NewScanner(strings.NewReader(input))
      	scanner.Split(bufio.ScanLines)
      
      	for scanner.Scan() {
      		fmt.Println(scanner.Text())
      	}
      	if err := scanner.Err(); err != nil {
      		fmt.Println("Error:", err)
      	}
      }
      //Hello,
      //world!
      
  4. bufio.ScanWords
    • 按照单词为单位进行分割。

    • 每次返回一个由非空白字符组成的连续片段。

    • func main() {
      	input := "Hello world"
      	scanner := bufio.NewScanner(strings.NewReader(input))
      	scanner.Split(bufio.ScanWords)
      
      	for scanner.Scan() {
      		fmt.Println(scanner.Text())
      	}
      	if err := scanner.Err(); err != nil {
      		fmt.Println("Error:", err)
      	}
      }
      //Hello
      //world
      
  5. bufio.ScanRegexp
    • 按照正则表达式匹配的内容进行分割。

    • 用户需要提供一个正则表达式作为参数。

    • func main() {
      	input := "apple123banana456cherry"
      	scanner := bufio.NewScanner(strings.NewReader(input))
      	scanner.Split(bufio.ScanRegexp(regexp.MustCompile(`\d+`)))//匹配一个或多个数字字符
      
      	for scanner.Scan() {
      		fmt.Println(scanner.Text())
      	}
      	if err := scanner.Err(); err != nil {
      		fmt.Println("Error:", err)
      	}
      }
      //123
      //456
      

4、自定义分割符(token)使用

最后如果上面的几个封装的都不够用,可以通过自定义token

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// 自定义分割函数,以逗号为分隔符
	scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
		// 查找第一个逗号的位置
		i := strings.IndexByte(string(data), ',')
		if i >= 0 {
			return i + 1, data[:i], nil
		}
		// 如果没有找到逗号,且已经到达输入流的末尾,则返回结束
		if atEOF {
			return len(data), data, nil
		}
		// 否则要求 Scan 方法继续读取数据
		return 0, nil, nil
	})

	for scanner.Scan() {
		fmt.Println("Token:", scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		fmt.Println("Error:", err)
	}
}

4、如果判断扫描到的数据是什么类型的数据

在 Go 中,标准输入的数据都是通过io.Reader以字节数组的形式暂存的。

那么怎么判断标准输入中的内容是字符串?还是整数?还是浮点数?..

  • 可以通过strconv包的函数来做判断
    1、判断是否为整数:
    
    val, err := strconv.Atoi(string(inputBytes))
    if err == nil {
        // inputBytes 包含一个整数
    } else {
        // inputBytes 不是整数
    }
    2、判断是否为浮点数:
    
    val, err := strconv.ParseFloat(string(inputBytes), 64)
    if err == nil {
        // inputBytes 包含一个浮点数
    } else {
        // inputBytes 不是浮点数
    }
    3、将其视为字符串:
    
    inputStr := string(inputBytes)
    // 现在 inputStr 包含从标准输入读取的字符串数据
    
  • 也可以使用类型断言从标准输入读取数据类型
    func main() {
        reader := bufio.NewReader(os.Stdin)
        inputBytes, _ := reader.ReadBytes('\n')
    
        // 使用类型断言判断数据类型
        switch v := interface{}(inputBytes).(type) {
        case int:
            fmt.Printf("Input is an integer: %d\n", v)
        case float64:
            fmt.Printf("Input is a float: %f\n", v)
        default:
            inputStr := string(inputBytes)
            fmt.Printf("Input is a string: %s\n", inputStr)
        }
    }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值