У меня есть доступ busybox 1.27.2
только к .
В настоящее время я обрабатываю словарь с более чем полумиллионом слов и более чем 6000 страниц (который был извлечен из PDF с помощью ghostscript и преобразован в обычный текст). Находится в файле 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.
при поиске нужного слова я получу:
На данный момент мне удалось добиться следующего:
- вход
->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.
Вот что я получаю при grep-анализе обычного текста после завершения извлечения с помощью ghostscript (обработано только с помощью 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) выполнены с текстом, при grepping я получил:
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, он просто продолжит обрабатывать последующие записи, как и первую.
Примечание: это означает, что весь входной файл должен поместиться в память — на современной системе с 16 ГБ или более оперативной памяти это вряд ли будет проблемой. На старой системе с недостаточным объемом оперативной памяти, но достаточным объемом подкачки, это все равно будет работать, но будет намного медленнее.
$ 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 излишне неудобным — строку поиска придется заключать в кавычки (из-за>
, который оболочка использует для перенаправления) и предварять--
(из-за-
, в противном случае grep будет воспринимать ваш шаблон поиска так, как будто вы подразумеваете их как опции). Например, вы не сможете просто сделать:grep ^firstword: dictionary.txt
Вместо этого вам придется сделать следующее:
grep -- '^->firstword:' dictionary.txt
Для лучшего примера я извлек текст из вашего изображения с помощьютессеракт-ocrи пропустите его через версию однострочного Perl, которая также удаляет все переводы строк, за которыми не следует ->
:
$ 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 решает проблему «использует огромное количество оперативной памяти». Вместо использования NUL в качестве разделителя входных записей используйте ->
в качестве разделителя. Это заставляет скрипт perl читать только одно определение слова за раз, а не весь файл сразу. Это заставит его работать намного быстрее с очень большим входным файлом, поскольку он не будет использовать всю доступную оперативную память и вызывать подкачку системы.
В скрипт необходимо внести и другие изменения, поскольку теперь мы обрабатываем последовательность символов, которая обозначаетначалонового определения слова какконецпредыдущего определения. В частности, нам теперь нужно:
- Измените параметр командной строки
-p
(всегда выводить текущую запись) на-n
(выводить текущую запись только тогда, когда мы это укажем). - Удалить символы конца строки (
chomp()
функция Perl делает это) - Проверьте, пуста ли входная запись или содержит только пробелы, поскольку теперь будетвоображаемыйпустая запись перед первой фактической записью "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/ /
), пока новая строка не начнется с ->
.