如何替换/删除新行(\n)?

如何替换/删除新行(\n)?

我只能访问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,这样每个单词的所有定义都会在很长的一行上,这是可以的。我可以将所有内容替换\ntr -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 所需的单词时。

到目前为止,我设法完成的是:

  1. 输入
->firstword: This is a defini-
tion.
->secondword: This is a second defini-
tion.
  1. 应用 tr -d '\n' < ./dictionary.txt > ./dictionary2.txt

  2. 输出是:

->firstword: This is a defini- tion. ->secondword: This is a second defini- tion.
  1. 跑:sed -e 's/->/\n->/g' ./dictionary2.txt

  2. 最终结果如下:

输出

->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

笔记:

  1. 字符-并不是唯一可能使用的“连字符”或“破折号”字符。维基百科有列出 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
  1. 我建议不要让每个单词的定义都以 开头->。这将使使用 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 命令行选项的详细信息,请参阅 )。

BEGINwhile(<>) { ..... }语句在脚本的开头、在使用 perl-p-n选项(这使得 perl 的行为有点像超级sedsed -n分别)引起的隐式循环之前和之外发生一次。类似地,END在读取和处理所有输入之后,语句在脚本末尾发生一次。

答案2

我建议使用以下模式在单个脚本中执行此操作N;P;D

sed -e ':loop' -e '$!N;/\n->/!s/-*\n/ /;tloop' -e 'P;D'

您循环添加 'N'ext 行并使用可选的破折号 ( ) 删除换行符s/-*\n/ /,直到新行以 开头->

相关内容