替换一个非常大的单行文件中的字符串
1. 概述
在处理文本文件时,一些程序会将一整行读入内存。如果输入文件有一个非常大的行,如果没有足够的内存来存储该行,程序可能会崩溃。
在本教程中,我们将看到如何替换一个非常大的单行文本文件中的字符串。例如,我们可能想要操作一个仅包含一行文本的 50GB 文件。由于某些程序无法处理非常大的单行文本文件,我们将看看我们有哪些替代方案。
2. 目标文件
一些现代 JavaScript 库将所有代码压缩成一行。假设我们有一个名为original.js的单行 JavaScript 文件,其中有一个错字。它调用“过滤器”方法而不是“过滤器”。我们将在以下部分修正这个错字。
3.使用tr和sed
我们可以使用tr 和sed 来替换该行。这将是拆分行和替换字符串的两步过程。
3.1. 分割长字符串
我们通常使用sed来替换字符串,但是sed会尝试将整行加载到内存中。为了克服这个问题,我们将把这条线分成多条更小的线。然后,我们将向sed提供这个新输入来替换字符串。最后,我们将输出重新加入一行。
在 Linux 中,默认情况下,行用换行符*“\n”分隔。在我们的例子中,我们将用“\n”替换另一个字符,并用这个新输入来馈送sed* 。我们必须选择一个不在我们要替换的字符串中的字符。此外,当我们用*“\n”*替换它时,输入文件必须包含字符并产生相对较小的行。
要将一行拆分为多行,我们可以使用程序tr将一个字符替换为“\n” **。tr一次处理一个字符,这意味着它可以毫无问题地处理带有大行的大文件。
tr有两个参数。它将用第二个参数替换第一个参数。让我们看看如何替换“;” 用*“\n”*:
$ echo "line one;line two" | tr ";" "\n"
line one
line two
如果我们的文件中有换行符,我们应该同时更改“;” 用*“\n”和“\n”用“;”。这样做将有助于保留原始换行符。为此,我们将运行tr “;\n” “\n;”* . 这样,tr会将第一个参数 (;) 中的第一个字符更改为第二个参数中的第一个字符 ( \n ),并将第一个参数中的第二个字符( \n ) 更改为第二个参数中的第二个字符(;)。
当我们向输入添加新行时,我们将重新加入这些行,因此输出与输入一致。这可以通过交换tr参数以产生反向替换来轻松完成。让我们使用tr “;\n” “\n;” 分割线,然后*tr “\n;” “;\n”*重新加入它:
$ echo "line one;line two" | tr ";\n" "\n;" | tr "\n;" ";\n"
line one;line two
我们可以看到,我们产生了相同的输入。
3.2. 替换字符串
将输入分成几行后,我们可以运行sed来替换字符串。让我们看看如何运行sed将字符串“Alan Turing”替换为“Alan Mathison Turing”:
$ echo "Alan Turing was born in London." | sed 's/Alan Turing/Alan Mathison Turing/'
Alan Mathison Turing was born in London.
到目前为止,我们已经了解了如何拆分长行、替换字符串以及重新加入行。最后,我们可以编写脚本来替换文件中行很长的字符串。
如果我们想在目标文件中将“ .fliter( ”替换为“ .filter( ”),我们可以选择字符“;”来分割行。“;”字符不在“ .fliter( ”字符串中,它是一个通常出现在 JavaScript 文件中的字符,所以它应该产生短行。让我们看看如何修复original.js 并将结果写入fixed.js:
$ tr ";\n" "\n;" < original.js | sed 's/\.fliter(/.filter(/' | tr "\n;" ";\n" > fixed.js
请注意,当我们将“ .fliter( ”替换为“ .filter (”时,我们必须转义点字符。这是因为sed的替换命令将正则表达式用于第一个参数。
4. 使用awk
还有其他程序可以替换文本文件中的字符串。我们可以使用awk 及其gsub函数来代替sed 。这将是配置awk的行分隔符并替换字符串的两步过程。
4.1. 更改行分隔符
使用awk,我们可以更改用于分隔行的字符。然后,我们可以使用另一个字符来生成更小的行,而不是使用默认的*“\n”*行分隔符。正如我们在上一节中看到的,我们必须选择一个不在我们想要替换的字符串中的字符。
要更改awk中使用的行分隔符,我们将在BEGIN块中将RS变量设置为所需的字符。例如,如果我们选择“;” 作为换行符,我们设置*RS=”;”。*让我们看看它是如何工作的:
$ echo "line one;line two" | awk 'BEGIN{RS=";"}{print}'
line one
line two
如上一节所述,我们必须产生与输入一致的输出。即使 awk 用“;”分割行 字符,输出必须与输入相同。我们可以看到awk的print函数写入了一个不在原始输入中的新行。
让我们改用printf函数,因此不添加换行符:
$ echo "line one;line two" | awk 'BEGIN{RS=";"}{printf "%s", $0}'
line oneline two
我们可以看到,我们只缺少“;” 特点。我们知道,除了第一行之外,所有行都以行分隔符开头。所以,让我们在前面加上“;” 字符到所有行,除非它是第一行:
$ echo "line one;line two" | awk 'BEGIN{RS=";"}{
if (NR != 1) {
printf "%c", RS
}
printf "%s", $0
}'
line one;line two
请注意,我们使用NR变量来获取当前行号并忽略第一行。此外,我们使用RS变量来打印行分隔符。
4.2. 替换字符串
我们看到了如何使用awk来处理包含除“ \n ”之外的任何字符的文件分割行。所以,我们现在可以用很长的行替换文件中的字符串。
要用awk替换字符串,我们将使用gsub函数。此函数的工作方式类似于sed的替换命令。它将第一个参数作为正则表达式并将其替换为第二个参数。然后,我们将调用gsub进行替换,然后使用上一个示例中的代码打印该行。
我们将重复上一节中的相同想法。让我们通过将“ .fliter( ”替换为“ .filter (” )来修复我们的目标文件:
$ awk 'BEGIN{RS=";"} {
gsub("\\.fliter\\(", ".filter(")
if (NR != 1) {
printf "%c", RS
}
printf "%s", $0
}' < original.js > fixed.js
请注意,当我们转义字符时, sed会有所不同。我们还必须转义“(”字符,并且必须使用双反斜杠。