我只能访问busybox 1.27.2
。
我目前正在处理一本超过 50 万单词、超过 6,000 页的词典(使用 Ghostscript 从 PDF 中提取并转换为纯文本)。位于一个20MB
.txt
文件中。最初,这本词典中的每个单词都有一个前导,->
以便更容易搜索单词。
我试图实现的目标是让它变得*nix
友好。这意味着如果我这样做:
grep -e '->myfancyword' ./dictionary.txt
.
我应该得到以下结果:
->fancyword: This is a very fancy word. *Definition going on for more than 6 lines*
这可以通过删除所有换行符轻松完成\n
,这样每个单词的所有定义都会在很长的一行上,这是可以的。我可以将所有内容替换\n
为tr -d '\n'
,然后得到该结果的输出,sed 's/->/\n->/g'
这样我就可以在一行中得到所有单词的定义。即使是这么大的文档,也能在 5 秒之内完成。
我几乎得到了我想要的结果,但并不完美。我可以做到这一点grep -e '->word' ./dictionary.txt
并获得这个词的完整定义。但它在外观上并不完美。
我对输出不满意的原因是,原始 pdf 的格式是打印在A4
页面上,这意味着当出现长单词时,它会被截断。像这样:
例如
->word: This is a defini-
tion.
如果我使用之前的工作流程处理文件,我会得到:->word: This is a defini- tion.
当 grep 所需的单词时。
到目前为止,我设法完成的是:
- 输入
->firstword: This is a defini-
tion.
->secondword: This is a second defini-
tion.
应用
tr -d '\n' < ./dictionary.txt > ./dictionary2.txt
输出是:
->firstword: This is a defini- tion. ->secondword: This is a second defini- tion.
跑:
sed -e 's/->/\n->/g' ./dictionary2.txt
最终结果如下:
输出
->firstword: This is a defini- tion.
->secondword: This is a second defini- tion.
在执行第二步之前,我想删除破折号和新行(-\n
)以将所有切碎的行“连接”在一起。
所以,我的问题是:如何替换/删除行尾包含破折号-
和换行符\n
( ) 的特定字符串?-\n
我喜欢得到的是:
输出(请检查破折号和空格 ( -
) 是否不再存在)
->firstword: This is a definition.
->secondword: This is a second definition.
谢谢。
编辑:
这是 PDF 文件的一页:
->abigeato. (Del lat. abigeatus). 1. m. Am. Hurto de ganado.
->abigeo. (Del lat. abigeus). 1. m. Am. Ladrón de ganado.
->abigotado, da. 1. adj. bigotudo.
->abinar. 1. tr. rur. y vulg. Binar la tierra.
->abintestato. (De ab intestato). 1. m. Der. Procedimiento judicial sobre herencia y
adjudicación de bienes de quien muere sin testar.
->abiogénesis. (De a-2, bio- y -génesis). 1. f. Producción hipotética de seres vivos par-
tiendo de materia inerte. 2. f. Bioquím. síntesis abiótica.
->abiótico, ca. 1. adj. Biol. Se dice del medio en que no es posible la vida. V. síntesis
abiótica
->abipón, na. 1. adj. Se dice del individuo de un pueblo amerindio que habitaba cerca del
Paraná. U. t. c. s. 2. adj. Perteneciente o relativo a los abipones. 3. m. Lengua de la familia
guaicurú hablada por los abipones.
->abisagrar. 1. tr. Clavar o fijar bisagras en las puertas y sus marcos, o en otros objetos.
->abisal. (Del lat. abyssus). 1. adj. abismal (|| perteneciente al abismo). 2. adj. Se dice de
las zonas del mar profundo que se extienden más allá del talud continental, y corresponden a
profundidades mayores de 2000 m. 3. adj. Perteneciente o relativo a tales zonas.
->abiselar. 1. tr. biselar.
->abisinio, nia. 1. adj. Natural de Abisinia, hoy Etiopía. U. t. c. s. 2. adj. Perteneciente o re-
lativo a este país de África. 3. m. Lengua abisinia. V. rito abisinio
->abismado, da. (Del part. de abismar). 1. adj. Dicho de una persona, de su expresión, de
su gesto, etc.: Ensimismados, reconcentrados. 2. adj. Heráld. Dicho de una pieza del escudo:
Puesta en el abismo.
->abismal (1). (Del ár. hisp. almismár, y este del ár. clás. mismar). 1. m. Cada uno de los
clavos con que se fijaba en el asta el hierro de la lanza.abismal2. 1. adj. Perteneciente o re-
lativo al abismo. 2. adj. Muy profundo, insondable, incomprensible.
->abismar. 1. tr. Hundir en un abismo. U. t. c. prnl. 2. tr. Confundir, abatir. U. t. c. prnl. 3.
prnl. Entregarse del todo a la contemplación, al dolor, etc. 4. prnl. Am. sorprenderse (|| con-
moverse con algo imprevisto o raro).
->abismático, ca. 1. adj. abismal2.
->abismo. (Quizá del lat. vulg. *abyssimus, der. de abyssus, y este del gr. , sin fondo). 1.
m. Profundidad grande, imponente y peligrosa, como la de los mares, la de un tajo, la de una
sima, etc. U. t. en sent. fig. Se sumió en el abismo de la desesperación. 2. m. infierno (|| lugar
de castigo eterno). 3. m. Cosa inmensa, insondable o incomprensible. 4. m. Diferencia
grande entre cosas, personas, ideas, sentimientos, etc. 5. m. Heráld. Punto o parte central
del escudo. 6. m. Nic. Maldad, perdición, ruina moral.
这是我在使用 Ghostscript 完成提取后 grep 常规文本时得到的结果(仅使用 dos2unix 处理):
grep -e '->abiog' ./rae-dos2unix.txt
->abiogénesis. (De a-2, bio- y -génesis). 1. f. Producción hipotética de seres vivos par-
这是在文本上完成前面的步骤(1-4)时,当 grep 时我得到:
grep -e '->abiog' ./rae-una-linea.txt
->abiogénesis. (De a-2, bio- y -génesis). 1. f. Producción hipotética de seres vivos par- tiendo de materia inerte. 2. f. Bioquím. síntesis abiótica.
答案1
这在 Perl 中相当容易。 perl 的-0
选项告诉它使用 NUL 字符而不是换行符作为输入记录分隔符,因此,除非输入中有 NUL 字符,否则它将把整个输入文件视为一条记录。即使有 NUL 字符,它也会继续处理后续记录,与第一个记录相同。
注意:这确实意味着整个输入文件必须适合内存 - 在具有 16GB 或更多 RAM 的现代系统上,这不太可能成为问题。在 RAM 不足但交换空间足够的旧系统上,它仍然可以工作,但速度会慢得多。
$ cat input.txt
->firstword: This is a defini-
tion.
->secondword: This is a second defini-
tion.
$ perl -0 -p -e 's/-\s*\n//g' input.txt
->firstword: This is a definition.
->secondword: This is a second definition.
这将删除每个连字符序列,后跟零个或多个空白字符(\s
,见下文),后跟换行符(\n
)。
正则表达式的部分\s*
用于匹配尾随空白字符可能位于行尾 - 根据我的经验,文本行具有尾随空格是很常见的(并且它们很难被发现,因为它们是非打印字符,即不可见)。或者,使用*
(零个或多个空间字符)或[ \t]*
(零个或多个空格或制表符)或\h*
(零个或多个水平的空白字符)而不是\s*
.
从man perlre
:
被视为空白的字符集是 Unicode 称为“模式空白”的字符,即:
U+0009 CHARACTER TABULATION U+000A LINE FEED U+000B LINE TABULATION U+000C FORM FEED U+000D CARRIAGE RETURN U+0020 SPACE U+0085 NEXT LINE U+200E LEFT-TO-RIGHT MARK U+200F RIGHT-TO-LEFT MARK U+2028 LINE SEPARATOR U+2029 PARAGRAPH SEPARATOR
笔记:
- 字符
-
并不是唯一可能使用的“连字符”或“破折号”字符。维基百科有列出 unicode 的页面连字符和短跑人物。幸运的是,perl 具有良好的 unicode 处理功能,因此可以重写单行代码以使用\p{Dash}
(或\p{Pd}
),而不是-
匹配所有破折号类别字符:
$ perl -0 -p -e 's/\p{Dash}\h*\n//g' input.txt
->firstword: This is a definition.
->secondword: This is a second definition.
然而,这会将破折号视为连字符(因此将删除行尾的破折号,与连字符相同)...并且使用破折号代替括号并不罕见。如果您不介意有关“连字符”被弃用的警告消息,您可以使用\p{Hyphen}
而不是。\p{Dash}
或者您可以使用括号表达式,其中仅包含您想要视为连字符的 unicode 代码点 - 例如
perl -0 -p -e 's/[\N{U+002D}\N{U+00AD}\N{U+2010}\N{U+2011}]\h*\n//g' input.txt
我建议不要让每个单词的定义都以 开头
->
。这将使使用 grep 搜索单词变得不必要的尴尬 - 搜索字符串必须用引号引起来(因为>
,shell 使用它进行重定向)并在前面加上--
(因为-
,否则 grep 会将您的搜索模式视为如果你的意思是它们是选项)。例如,您将无法仅执行以下操作:grep ^firstword: dictionary.txt
相反,你必须这样做:
grep -- '^->firstword:' dictionary.txt
为了更好的例子,我从你的图像中提取了文本tesseract-ocr并通过 perl one-liner 的一个版本运行它,该版本还删除所有后面没有跟随的换行符->
:
$ cat input2.txt
->abigeato. (Del lat. abigeatus). 1. m. Am. Hurto de ganado.
->abigeo. (Del lat. abigeus). 1. m. Am. Ladrén de ganado.
->abigotado, da. 1. adj. bigotudo.
->abinar. 1. tr. rur. y vulg. Binar la tierra.
->abintestato. (De ab intestato). 1. m. Der. Procedimiento judicial sobre herencia y
adjudicacion de bienes de quien muere sin testar.
Eiiftiénesis. (De a-2, bio- y -génesis). 1. f. Produccién hipotética de seres vivos par-
tiendo de materia inerte. 2. f. Bioquim. sintesis abistica,
->abidtico, ca. 1. adj. Biol. Se dice del medio en que no es posible la vida. V. sintesis
abidtica
->abipon, na. 1. adj. Se dice del individuo de un pueblo amerindio que habitaba cerca del
Parana. U. t.c. s. 2. adj. Perteneciente o relativo a los abipones. 3. m. Lengua de la familia
guaicurt hablada por los abipones.
->abisagrar. 1. tr. Clavar o fijar bisagras en las puertas y sus marcos, 0 en otros objetos.
->abisal. (Del lat. abyssus). 1. adj. abismal (|| perteneciente al abismo). 2. adj. Se dice de
las zonas del mar profundo que se extienden mas alla del talud continental, y corresponden a
$ perl -0 -p -e 's/[\N{U+002D}\N{U+00AD}\N{U+2010}\N{U+2011}]\h*\n//g; s/\n+(?!->)//g' input2.txt
->abigeato. (Del lat. abigeatus). 1. m. Am. Hurto de ganado.
->abigeo. (Del lat. abigeus). 1. m. Am. Ladrén de ganado.
->abigotado, da. 1. adj. bigotudo.
->abinar. 1. tr. rur. y vulg. Binar la tierra.
->abintestato. (De ab intestato). 1. m. Der. Procedimiento judicial sobre herencia yadjudicacion de bienes de quien muere sin testar.Eiiftiénesis. (De a-2, bio- y -génesis). 1. f. Produccién hipotética de seres vivos partiendo de materia inerte. 2. f. Bioquim. sintesis abistica,
->abidtico, ca. 1. adj. Biol. Se dice del medio en que no es posible la vida. V. sintesisabidtica
->abipon, na. 1. adj. Se dice del individuo de un pueblo amerindio que habitaba cerca delParana. U. t.c. s. 2. adj. Perteneciente o relativo a los abipones. 3. m. Lengua de la familiaguaicurt hablada por los abipones.
->abisagrar. 1. tr. Clavar o fijar bisagras en las puertas y sus marcos, 0 en otros objetos.
->abisal. (Del lat. abyssus). 1. adj. abismal (|| perteneciente al abismo). 2. adj. Se dice delas zonas del mar profundo que se extienden mas alla del talud continental, y corresponden a
我仍然建议->
从最终输出文件中删除该序列。它在处理文本时是一个有用的标记,但之后会出现问题。
@zevzek 的评论解决了“使用大量 RAM”的问题。不要使用 NUL 作为输入记录分隔符,而是使用->
as 分隔符。这使得 perl 脚本一次只读取一个单词定义,而不是一次读取整个文件。这将使它在处理非常大的输入文件时运行得更快,因为它不会使用所有可用的 RAM 并导致系统交换。
脚本还需要进行其他更改,因为我们现在正在处理标记开始一个新词的定义为结尾前面的定义。具体来说,我们现在需要:
- 将命令行选项
-p
(始终输出当前记录)更改为-n
(仅在我们指定时输出当前记录)。 - 删除行尾字符(perl 的
chomp()
函数就是这样做的) - 检查输入记录是否为空或仅包含空格,因为现在将有一个假想第一个实际记录“abigeato”之前有空记录,我们不想将其打印出来。 (为什么突然出现一个想象中的空记录?因为
->
now表示一条记录的结束,而不是新一条记录的开始。in->
是->abigeato
前一条(空)记录和新的“abigeato”记录之间的分隔符) - 使用“->”和换行符打印修改后的记录。
总而言之,这些将改变最后的一句台词:
$ perl -0 -p -e 's/[\N{U+002D}\N{U+00AD}\N{U+2010}\N{U+2011}]\h*\n//g;
s/\n+(?!->)//g' input2.txt
对此:
perl -n -e 'BEGIN { $/="->" };
chomp;
next if m/^\s*$/;
s/[\N{U+002D}\N{U+00AD}\N{U+2010}\N{U+2011}]\h*\n//g;
s/\n+//g;
print "->$_\n"' input2.txt
此版本的输出与原始版本相同,只是最终输出行保证以换行符 ( \n
) 结尾。原始版本并没有保证这一点,事实上,它通过删除所有后面没有跟随的换行符来阻止它->
。这是一个免费的奖励,因为从技术上讲,如果每行都以 ... 结尾,则文件只是 unix 中的文本文件。\n
大多数情况下,这并不重要(至少,对于标准文本处理工具的现代版本来说不是) ,但是如果“文本文件”的最后一行不以\n
.结尾,某些程序将无法正确处理它。
(顺便说一句,原来的问题可以通过添加 END 块来修复,将换行符添加回输出的末尾END { print "\n" }
:)
$/
是一个 perl 变量,它定义输入记录分隔符(man perlvar
有关 perl 预定义/特殊/控制变量的详细信息,请参阅 ),类似于RS
中的变量awk
。以前,我使用 perl 的-0
选项将其设置为 NUL 字符(man perlrun
有关 perl 命令行选项的详细信息,请参阅 )。
BEGIN
while(<>) { ..... }
语句在脚本的开头、在使用 perl-p
或-n
选项(这使得 perl 的行为有点像超级sed
或sed -n
分别)引起的隐式循环之前和之外发生一次。类似地,END
在读取和处理所有输入之后,语句在脚本末尾发生一次。
答案2
我建议使用以下模式在单个脚本中执行此操作N;P;D
:
sed -e ':loop' -e '$!N;/\n->/!s/-*\n/ /;tloop' -e 'P;D'
您循环添加 'N'ext 行并使用可选的破折号 ( ) 删除换行符s/-*\n/ /
,直到新行以 开头->
。