我只能訪問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
) 結尾。原始版本並沒有保證這一點,事實上,它通過刪除所有後面沒有跟隨的換行符來阻止它->
。\n
這是一個免費的獎勵,因為從技術上講,如果每行都以 ... 結尾,則文件只是 unix 中的文本文件。版本來說不是) ,但是如果“文字檔案”的最後一行不以\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/ /
,直到新行以 開頭->
。