前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >go 读取文件

go 读取文件

作者头像
solate
发布于 2019-07-22 08:33:52
发布于 2019-07-22 08:33:52
2.4K00
代码可运行
举报
文章被收录于专栏:solate 杂货铺solate 杂货铺
运行总次数:0
代码可运行

按字节读取

将整个文件读入内存

标准库提供了多种函数和实用程序来读取文件数据。

这意味着两个先决条件:

  1. 该文件必须适合内存
  2. 我们需要知道文件的大小,以便实例化一个足够大的缓冲区来保存它。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
file, err := os.Open("filetoread.txt")
if err != nil {
  fmt.Println(err)
  return
}
defer file.Close()

fileinfo, err := file.Stat()
if err != nil {
  fmt.Println(err)
  return
}

filesize := fileinfo.Size()
buffer := make([]byte, filesize)

bytesread, err := file.Read(buffer)
if err != nil {
  fmt.Println(err)
  return
}

fmt.Println("bytes read: ", bytesread)
fmt.Println("bytestream to string: ", string(buffer))

以块的形式读取文件

在大多数情况下,一次读取文件是有效的,但有时候我们会希望使用多块内存来读取文件。

读一个大小的文件,处理,并重复,直到结束

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const BufferSize = 100
file, err := os.Open("filetoread.txt")
if err != nil {
  fmt.Println(err)
  return
}
defer file.Close()

buffer := make([]byte, BufferSize)

for {
  bytesread, err := file.Read(buffer)

  if err != nil {
    if err != io.EOF {
      fmt.Println(err)
    }

    break
  }

  fmt.Println("bytes read: ", bytesread)
  fmt.Println("bytestream to string: ", string(buffer[:bytesread]))
}

与完全读取文件相比,主要区别在于:

  1. 我们读取,直到我们得到一个EOF标记,所以我们添加一个特定的检查 err == io.EOF。
  2. 我们定义缓冲区大小,所以我们可以控制我们想要的“块”大小。这可以提高正确使用时的性能,因为操作系统使用高速缓存正在读取的文件。
  3. 如果文件大小不是缓冲区大小的整数倍,则最后一次迭代将只将剩余的字节数添加到缓冲区,从而调用buffer[:bytesread]。在正常情况下, bytesread将与缓冲区大小相同。

对于循环的每一次迭代,内部文件指针被更新。当下一次读取发生时,从文件指针偏移开始的数据返回到缓冲区的大小。所有读取/读取调用在内部翻译成系统调用并发送到内核,内核管理这个指针。

同时读取文件块

如果我们想要加快上面提到的块的处理呢?一种方法是使用多个go routines!

使用ReadAt与read是有一些区别的。

注意:不限制goroutine的数量,它只是由缓冲区大小来定义的。事实上,这个数字可能有一个上限。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const BufferSize = 100
file, err := os.Open("filetoread.txt")
if err != nil {
  fmt.Println(err)
  return
}
defer file.Close()

fileinfo, err := file.Stat()
if err != nil {
  fmt.Println(err)
  return
}

filesize := int(fileinfo.Size())
// Number of go routines we need to spawn.
concurrency := filesize / BufferSize

// check for any left over bytes. Add one more go routine if required. //如果没除尽,就要加1
if remainder := filesize % BufferSize; remainder != 0 {
  concurrency++
}

var wg sync.WaitGroup
wg.Add(concurrency)

for i := 0; i < concurrency; i++ {
  go func(chunksizes []chunk, i int) {
    defer wg.Done()

    chunk := chunksizes[i]
    buffer := make([]byte, chunk.bufsize)
    bytesread, err := file.ReadAt(buffer, chunk.offset)

    // As noted above, ReadAt differs slighly compared to Read when the
    // output buffer provided is larger than the data that's available
    // for reading. So, let's return early only if the error is
    // something other than an EOF. Returning early will run the
    // deferred function above
    if err != nil && err != io.EOF {
      fmt.Println(err)
      return
    }

    fmt.Println("bytes read, string(bytestream): ", bytesread)
    fmt.Println("bytestream to string: ", string(buffer[:bytesread]))
  }(chunksizes, i)
}

wg.Wait()

注意:始终检查返回的字节数,并重新输出缓冲区。

扫描

可以实现通过使用类似 Scanner类型,和相关的功能bufio包。

这个bufio.Scanner类型实现了一个“分割”功能的函数,并根据这个函数前进一个指针。

如bufio.ScanLines,对于每一次迭代,内置的分割函数将指针推进到下一个换行符

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
file, err := os.Open("filetoread.txt")
if err != nil {
  fmt.Println(err)
  return
}
defer file.Close()

scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)

// Returns a boolean based on whether there's a next instance of `\n`
// character in the IO stream. This step also advances the internal pointer
// to the next position (after '\n') if it did find that token.
read := scanner.Scan()

if read {
  fmt.Println("read byte array: ", scanner.Bytes())
  fmt.Println("read string: ", scanner.Text())
}

// goto Scan() line, and repeat

逐行扫描

要以行的方式读取整个文件,可以使用类似这样的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
file, err := os.Open("filetoread.txt")
if err != nil {
  fmt.Println(err)
  return
}
defer file.Close()

scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)

// This is our buffer now
var lines []string

for scanner.Scan() {
  lines = append(lines, scanner.Text())
}

fmt.Println("read lines:")
for _, line := range lines {
  fmt.Println(line)
}

逐个单词扫描

该bufio软件包包含基本的预定义分割功能

  1. ScanLines(默认)
  2. ScanWords
  3. ScanRunes(对于遍历UTF-8码点非常有用,而不是字节)
  4. ScanBytes

所以要读取文件,并在文件中创建一个单词列表,可以使用类似这样的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
file, err := os.Open("filetoread.txt")
if err != nil {
  fmt.Println(err)
  return
}
defer file.Close()

scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords)

var words []string

for scanner.Scan() {
	words = append(words, scanner.Text())
}

fmt.Println("word list:")
for _, word := range words {
	fmt.Println(word)
}

该ScanBytes分离功能将使输出作为我们在前期已经看到相同的Read()例子

主要区别:每次我们需要在扫描的情况下追加 字节/字符串 数组时,动态分配的问题

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
file, err := os.Open("filetoread.txt")
if err != nil {
  fmt.Println(err)
  return
}
defer file.Close()

scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords)

// initial size of our wordlist
bufferSize := 50
words := make([]string, bufferSize)
pos := 0

for scanner.Scan() {
  if err := scanner.Err(); err != nil {
    // This error is a non-EOF error. End the iteration if we encounter
    // an error
    fmt.Println(err)
    break
  }

  words[pos] = scanner.Text()
  pos++

  if pos >= len(words) {
    // expand the buffer by 100 again
    newbuf := make([]string, bufferSize)
    words = append(words, newbuf...)
  }
}

fmt.Println("word list:")
// we are iterating only until the value of "pos" because our buffer size
// might be more than the number of words because we increase the length by
// a constant value. Or the scanner loop might've terminated due to an
// error prematurely. In this case the "pos" contains the index of the last
// successful update.
for _, word := range words[:pos] {
  fmt.Println(word)
}

所以我们最终只做了很少的片段“增长”操作,但是根据bufferSize 文件中字数的不同,我们最终可能会得到一些空的插槽

将一个长串分成单词

bufio.NewScanner作为一个参数,需要一个满足一个io.Reader接口的类型 ,这意味着它可以处理任何具有Read定义的方法的类型 。标准库中返回“reader”类型的字符串实用程序方法之一是strings.NewReader 函数。从字符串中读出单词时,我们可以将它们结合起来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
file, err := os.Open("_config.yml")
longstring := "This is a very long string. Not."
handle(err)

var words []string

scanner := bufio.NewScanner(strings.NewReader(longstring))
scanner.Split(bufio.ScanWords)

for scanner.Scan() {
	words = append(words, scanner.Text())
}

fmt.Println("word list:")
for _, word := range words {
	fmt.Println(word)
}

扫描逗号分隔的字符串

用基本file.Read()或 Scanner类型手动解析CSV文件/字符串是非常麻烦的,因为根据分割函数的“word” bufio.ScanWords被定义为由Unicode空格分隔的一堆符文。

读取个别符文,并跟踪缓冲区的大小和位置(如lexing / parsing中所做的)

我们可以定义一个新的分割功能,直到遇到读者一个逗号读取字符,然后返回块时,Text()或者Bytes()被调用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
  1. data 是输入字节的字符串
  2. atEOF 是传递给函数的标志,表示令牌的结尾
  3. advance使用它我们可以指定要处理的位置数作为当前的读取长度。扫描循环完成后,此值用于更新光标位置
  4. token 是扫描操作的实际数据
  5. err 如果你想表示一个问题。

为了简单起见,我展示了一个读取字符串的例子,而不是一个文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
csvstring := "name, age, occupation"

// An anonymous function declaration to avoid repeating main()
ScanCSV := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
  commaidx := bytes.IndexByte(data, ',')
  if commaidx > 0 {
    // we need to return the next position
    buffer := data[:commaidx]
    return commaidx + 1, bytes.TrimSpace(buffer), nil
  }

  // if we are at the end of the string, just return the entire buffer
  if atEOF {
    // but only do that when there is some data. If not, this might mean
    // that we've reached the end of our input CSV string
    if len(data) > 0 {
      return len(data), bytes.TrimSpace(data), nil
    }
  }

  // when 0, nil, nil is returned, this is a signal to the interface to read
  // more data in from the input reader. In this case, this input is our
  // string reader and this pretty much will never occur.
  return 0, nil, nil
}

scanner := bufio.NewScanner(strings.NewReader(csvstring))
scanner.Split(ScanCSV)

for scanner.Scan() {
  fmt.Println(scanner.Text())
}

读取文件到缓冲区

如果你只是想读一个文件到缓冲区呢, 使用 ioutil

读整个文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bytes, err := ioutil.ReadFile("_config.yml")
if err != nil {
  log.Fatal(err)
}

fmt.Println("Bytes read: ", len(bytes))
fmt.Println("String read: ", string(bytes))

读取整个文件目录

如果你有大文件,不要运行这个脚本

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
filelist, err := ioutil.ReadDir(".")
if err != nil {
  log.Fatal(err)
}

for _, fileinfo := range filelist {
  if fileinfo.Mode().IsRegular() {
    bytes, err := ioutil.ReadFile(fileinfo.Name())
    if err != nil {
      log.Fatal(err)
    }
    fmt.Println("Bytes read: ", len(bytes))
    fmt.Println("String read: ", string(bytes))
  }
}

更多辅助功能

在标准库中有更多的功能来读取文件(或者更准确地说,是一个Reader)。

  1. ioutil.ReadAll() - >采取一个类似于io的对象,并将整个数据作为字节数组返回
  2. io.ReadFull()
  3. io.ReadAtLeast()
  4. io.MultiReader - >一个非常有用的基元来组合多个类似io的对象。所以你可以有一个要读取的文件列表,并将它们视为一个连续的数据块,而不是管理在每个以前的对象末尾切换文件对象的复杂性。

错误修复

为了突出显示“读取”功能,我选择了使用打印出来并关闭文件的错误函数的路径:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func handleFn(file *os.File) func(error) {
  return func(err error) {
    if err != nil {
      file.Close()
      log.Fatal(err)
    }
  }
}

// inside the main function:
file, err := os.Open("filetoread.txt")
handle := handleFn(file)
handle(err)

这样做,错过了一个关键的细节:当没有错误,程序运行完成时,我没有关闭文件句柄。如果程序运行多次而不会引发任何错误,则会导致文件描述符泄漏。

我的初衷是避免defer因为内部log.Fatal调用 os.Exit不运行递延函数,所以我选择了明确的关闭文件,但是后来错过了另一个成功运行的情况。

我已经更新了要使用的示例defer,而return不是依靠os.Exit()。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
可不可以教下我 今天在网上弄了一天还是弄不了 这个wsa
可不可以教下我 今天在网上弄了一天还是弄不了 这个wsa
回复回复点赞举报
推荐阅读
windows11运行安卓apk应用
输入https://www.microsoft.com/store/productid/9p3395vx91nr,后面的下拉框选择 「slow」,点击「✓」按钮
华创信息技术
2022/05/28
3.4K0
windows11运行安卓apk应用
物理机安装 Windows 11
本文章首发于语雀! 通过各种高科技功能同步到Hajeekn 的博客 最近 Windows 11 发布了,我趁机白嫖了个预览版镜像在虚拟机里面装着玩玩,之后便给实体机也装上了 本文章说说如何安装 Windows 11 和对于笔记本如何优化动效
FloatSheep
2022/04/25
1.7K0
物理机安装 Windows 11
Windows 11 安装Android子系统 使用安卓APP教程
最近,微软发布了适用于 Android 的 Windows 子系统(WSA)的预览版本,可以直接在 Windows 桌面运行 Android 应用。该预览版本首先在美国的 Beta 通道(Windows 11 Build 22000.xxx 系列版本)中提供。
德顺
2023/08/25
5.3K0
Windows 11 安装Android子系统 使用安卓APP教程
VMware虚拟机黑群晖7.2 Beta (懒人包)
1. 首先解压安装包,双击解压出来的 DS923.vmx 配置文件. 导入到虚拟机
素颜520
2023/04/22
3.8K1
VMware虚拟机黑群晖7.2 Beta (懒人包)
B4A如何运行在Win11安卓子系统上
在开始菜单,点击Windows Subsystem for Android突变进入子系统进行配置
办公魔盒
2023/03/02
1.7K0
B4A如何运行在Win11安卓子系统上
Windows 11如何使用 Android子系统
适用于 Android 的 Windows 子系统包括 Linux 内核和基于 Android 开源项目(AOSP)版本 11 的 Android 操作系统(即 Android 11)。
剑指工控
2022/03/11
2.5K0
Windows 11如何使用 Android子系统
从零开发一款相机APP 第三篇:开发环境安装
这个得配置了,才能使用adb、fastboot命令,才能编译android工程项目。
小驰行动派
2022/09/23
8190
从零开发一款相机APP  第三篇:开发环境安装
React Native学习笔记(一)—— Win11安卓子系统的安装与使用 - Windows Subsystem for Android - WSA
写这篇文章的目的是为了学习React Native开发打基础,能够更好的运行、测试原生应用,带来比模拟器更好的体验。
张果
2023/03/14
3.3K0
React Native学习笔记(一)—— Win11安卓子系统的安装与使用 - Windows Subsystem for Android - WSA
【2023最新版】Windows11家庭版:安卓子系统(WSA)安装及使用教程【全网最详细】
Windows Subsystem for Android™ with Amazon Appstore - Microsoft Store Apps
Qomolangma
2024/07/29
1.5K0
【2023最新版】Windows11家庭版:安卓子系统(WSA)安装及使用教程【全网最详细】
Win10 家庭版安装Docker失败?试试这个解决步骤
Win10 家庭版缺少虚拟化功能组件,因此默认Docker是无法安装启动的,那首先要做的就是在环境上提供完善了。
做棵大树
2022/09/27
3.5K0
Win10 家庭版安装Docker失败?试试这个解决步骤
生物医学领域科研工作者的软件和工具清单-3.0版本
微软的Office和金山的WPS,大家不陌生,应该都有。WPS免费,Office需要破解,这里提供破解版下载地址:
DoubleHelix
2020/07/03
3.3K0
生物医学领域科研工作者的软件和工具清单-3.0版本
教程 - Win 11 安装 wsa 安卓虚拟机
Windows Subsystem for Android 可用于 Windows 11 上的公共预览版。
acc8226
2022/05/17
1.7K0
如何在Linux快速搭建一套ADB环境
Android Debug Bridge,安卓调试桥,它借助adb.exe(Android SDK安装目录platform-tools下),用于电脑端与模拟器或者真实设备交互;使用adb命令需安装Android SDK,并配置环境变量;
大刚测试开发实战
2022/11/14
2.1K0
如何在Linux快速搭建一套ADB环境
android-studio安装及android开发环境搭建[通俗易懂]
android-studio-IDE:https://developer.android.google.cn/studio/
全栈程序员站长
2022/09/15
1.1K0
windows10离线安装docker并使用linux子系统部署nacos作为注册中心
4、下载ubutu安装包并解压,这个是Ubuntu_1804.2019.522.0_x64版本。将下载的安装包为appx的扩展名改为zip。可以从这个地址下载:见文末。然后解压。用管理员身份执行ubuntu.exe程序。等几分钟输入用户名和密码。
jiankang666
2022/05/12
3.7K0
windows10离线安装docker并使用linux子系统部署nacos作为注册中心
QtScrcpy,人人可用的多屏协同
他们的缺点显而易见,必须使用特定品牌的手机,甚至特定的品牌的电脑才可以。另外还有什么高延迟,只支持windows系统等等。
福贵
2020/09/10
3.9K0
安装了anaconda 还需要单独安装pycharm吗_ugnx10安装教程
张伟伟,男,西安工程大学电子信息学院,2019级硕士研究生,张宏伟人工智能课题组。 研究方向:机器视觉与人工智能。 电子邮件:zhangweiweicpp@163.com 个人CSDN主页:欢迎关注和相互交流学习.
全栈程序员站长
2022/09/27
6820
安装了anaconda 还需要单独安装pycharm吗_ugnx10安装教程
Win11及WSL2安装和终端美化
链接:https://pan.baidu.com/s/1RZteNm-AoJoB8zH2Zs899g 提取码:wins
孔西皮
2023/10/18
1.7K0
Win11及WSL2安装和终端美化
Windows Subsystem for Android 安装<简化向>[通俗易懂]
1. 电脑是Win11 Beta / Dev 版本或在22000系统以上,且可以在BIOS中开启虚拟化
全栈程序员站长
2022/09/13
2.1K0
Windows Subsystem for Android 安装<简化向>[通俗易懂]
把Android系统签名弄成jks
假设我们得到了系统签名文件:platform.pk8、platform.x509.pem,还需要一个用于签名的文件:signapk.jar,这里提供了一份下载连接,可供练习使用:链接:https://pan.baidu.com/s/1OiBcVyhZVqTulb6HXwcqHA 提取码:7g81
全栈程序员站长
2022/08/31
2.1K0
把Android系统签名弄成jks
推荐阅读
相关推荐
windows11运行安卓apk应用
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档