字符串缓冲区

简介: 字符串缓冲区

假如我们正在开发一段处理字符串的程序,比如逐行地读取一个文件。典型的代码可能形如:

local buff = ""
for line in io.lines() do
  buff = buff .. line .. "\n"
end


虽然这段 Lua 语言代码看似能够正常工作,但实际上在处理大文件时却可能导致巨大的性能开销。例如,在一台电脑上用这段代码读取一个 4.5MB 大小的文件需要超过 30 秒的时间。我们来分析以下原因,假设每行有 20 个字节,当我们读取了大概 2500 行后, buff 就会变成一个 50KB 大小的字符串。在 Lua 语言中进行字符串连接 buff .. line .. "\n" 时,会创建一个 50020 字节的新字符串,然后从 buff 中复制 50000 字节中到这个新字符串。这样,对于后续的每一行, Lua 语言都需要移动大概 50KB 且还在不断增长的内存。因此,该算法的时间复杂度是二次方的。在读取了 100 行(仅 2K B)以后, Lua 语言就已经移动了至少 5MB 内存。当 Lua 语言完成了 350KB 的读取后,它已经至少移动了 50GB 的数据。(这个问题不是 Lua 语言特有的:在其他语言中,只要字符串是不可变值,就会出现类似的问题,其中最有名的例子就是 Java


在现实场景中,上述情况的发生是很小概率的事件,对于较小的字符串,上述循环并没什么问题。当读取整个文件时, Lua 语言提供了带有参数的函数 io.read("a") 来一次性地读取整个文件。不过,有时候我们必须面对这个问题。 Java 提供了 StringBuffer 类来解决这个问题,而在 Lua 语言中,我们可以把一个表当做字符串缓冲区,其关键是使用函数 table.concat ,这个函数会将指定列表中的所有字符串连接起来并返回连接后的结果。使用函数 concat 可以这样重写上述循环:

local t = {}
for line in io.lines() do
  t[#t + 1] = line .. "\n"
end
local s = table.concat(t)


之前的代码读取同样的文件需要超过半分钟,而上述实现则只需要不到 0.05 秒。(不过尽管如此,读取整个文件最好还是使用带有参数 "a"io.read 函数。)

继续优化,函数 concat 还有第二个可选参数,用于指定插在字符串间的分隔符。有了这个分隔符,我们就不必在每行后插入换行符了:

local t = {}
for line in io.lines() do
  t[#t + 1] = line
end
local s = table.concat(t, "\n") .. "\n"


虽然函数 concat 能够在字符串之间插入分隔符,但我们还需要增加最后一个换行符。最后一次字符串连接创建了结果字符串的一个副本,这个副本可能已经相当长了。虽然没有直接的选项能够让函数 concat 插入这个额外的分隔符,但可以想办法绕过,只需要在字符串 t 后面添加一个空字符串就行了:

t[#t + 1] = ""
s = table.concat(t, "\n")


现在,正如我们所期望的那样,函数 concat 会在结果字符串的最后添加一个换行符。

目录
相关文章
|
6月前
|
缓存 API C语言
文件的缓冲区
文件的缓冲区
76 1
|
6月前
|
存储 C语言
文件缓冲区
文件缓冲区
52 0
|
11天前
将字符串写入文件
将字符串写入文件。
19 2
|
4月前
|
Java C++ Python
使用getline()从文件中读取一行字符串
C++ 中的 `getline()` 函数用于从文件流中读取整行文本。它可以从 `fstream` 和 `ifstream` 对象中调用。有两种语法形式:一种读取到 `\n` 或达到指定缓冲区大小,另一种允许指定自定义分隔符。如果文件流中的字符数量超过缓冲区大小,可能导致读取失败。示例代码展示了如何使用 `getline()` 读取单行和多行文本。
|
6月前
|
存储 缓存 小程序
详细讲解缓冲区
详细讲解缓冲区
|
6月前
|
Java C++ Python
C++ 使用getline()从文件中读取一行字符串
`getline()` 是 C++ 中 `istream` 类的一个方法,被 `fstream` 和 `ifstream` 继承,用于从文件中读取一行字符串。它有两种语法:一种是从文件读取 `bufSize-1` 个字符到 `buf` 直到 `\n`,另一种是读到指定分隔符 `delim`。如果文件中的字符数量超过 `bufSize`,会导致读取失败。示例代码展示了如何使用 `getline()` 读取和打印文件内容。通过循环调用 `getline()`,可以连续读取文件的多行数据。
115 0
C++读取单个字符操作
get() 是 istream 类的成员函数,它有多种重载形式,不过本文只介绍最简单最常用的一种: int get(); 此函数从输入流中读入一个字符,返回值就是该字符的 ASCII 码。 如果碰到输入的末尾,则返回值为 EOF。EOF 是 End of File 的缩写。istream 类中从输入流(包括文件)中读取数据的成员函数,在把输入数据都读取完后再进行读取,就会返回 EOF。 EOF 是在 iostream 类中定义的一个整型常量,值为 -1。get() 函数不会跳过空格、制表符、回车等特殊字符,所有的字符都能被读入。例如下面的程序: #include <iostr
|
Java C++ Python
C++ 使用getline():从文件中读取一行字符串
getline() 方法从 cin 输入流缓冲区中读取一行字符串。在此基础上,getline() 方法还适用于读取指定文件中的一行数据,本节就给大家做详细的讲解。 我们知道,getline() 方法定义在 istream 类中,而 fstream 和 ifstream 类继承自 istream 类,因此 fstream 和 ifstream 的类对象可以调用 getline() 成员方法。 当文件流对象调用 getline() 方法时,该方法的功能就变成了从指定文件中读取一行字符串。该方法有以下 2 种语法格式: istream & getline(char* buf, int bufS
239 0
|
C语言
理解缓冲区
理解缓冲区
104 0
|
前端开发
字符串与Buffer
字符串与Buffer
62 0