Я пытаюсь исключить определенные слова из файла словаря.
# cat en.txt
test
testing
access/p
batch
batch/n
batches
cross
# cat exclude.txt
test
batch
# grep -vf exclude.txt en.txt
access/p
cross
В результаты должны быть включены такие слова, как «тестирование» и «партии».
expected result:
testing
access/p
batches
cross
Потому что за словом «партия» может следовать или не следовать косая черта «/». После косой черты может быть один или несколько тегов (в данном случае n). Но слово «партии» — это другое слово, и оно не должно совпадать с «партией».
У вас может быть такая строчка, как foo/batch
в en.txt
? Если да, то должен ли он быть выведен или не с учетом вашего опубликованного exlude.txt
? Может ли exclude.txt
содержать какие-либо отдельные буквы, такие как n
, например, в batch/n
?
Если в en.txt есть слово «тест/партия», его не следует включать в результаты. В файле exclude.txt есть только слова без тегов (hunspell).
@TLP попробовал параметр -w. Но это не дает правильных результатов, потому что мой файл содержит символы Юникода, а grep не полностью совместим.
@shantanuo Возможно, это была довольно важная информация для вашего вопроса. Почему бы вам исключить это?
Я бы использовал GNU AWK
для этой задачи следующим образом, пусть en.txt
контент будет
test
testing
access/p
batch
batch/n
batches
cross
и exclude.txt
содержание быть
test
batch
затем
awk 'BEGIN{FS = "/"}FNR==NR{arr[$1];next}!($1 in arr)' exclude.txt en.txt
дает вывод
testing
access/p
batches
cross
Объяснение: я сообщаю GNU AWK
, что /
является разделителем полей (FS
), затем при обработке первого файла (где количество строк глобально равно количеству строк внутри файла, то есть FNR==NR
) я просто использую значение 1-го столбца в качестве ключа в массиве arr
а затем перейти к строке next
, так что больше ничего не происходит, для 2-й (и следующих файлов, если они есть) я выбираю строки, чей 1-й столбец не является (!
) одним из ключей массива arr
.
(проверено в GNU Awk 5.0.1)
Поскольку в словаре есть много слов, которые могут иметь корень в одном из тех, которые нужно исключить, мы не можем удобно† использовать поисковый хеш (построенный из списка исключений), но должны проверять их все. Один из способов сделать это более эффективно — использовать шаблон чередования, построенный на основе списка исключений.
use warnings;
use strict;
use feature 'say';
use Path::Tiny; # to read ("slurp") a file conveniently
my $excl_file = 'exclude.txt';
my $re_excl = join '|', split /\n/, path($excl_file)->slurp;
$re_excl = qr($re_excl);
while (<>) {
if ( m{^ $re_excl (?:/.)? $}x ) {
# say "Skip printing (so filter out): $_";
next;
}
say;
}
Это используется как program.pl dictionary-filename
и печатает отфильтрованный список.
Здесь я предположил, что за корневым словом для исключения может следовать /
, за которым следует один символ, (?:/.)?
, так как примеры в вопросе используют это, и по этому поводу нет точного утверждения. Шаблон также предполагает отсутствие пробелов вокруг слова.
Пожалуйста, отрегулируйте по мере необходимости для того, что может на самом деле последовать /
. Например, это будет (?:/.+)?
хотя бы для одного персонажа, (?:/[np])?
для любого персонажа из определенного списка (n
или p
), (?:[^xy]+)?
для любого персонажа, не входящего в данный список, и т. д.
Оператор qr формирует правильный шаблон регулярного выражения.
† Может по-прежнему сначала удалять окончания слов, затем использовать поиск, а затем возвращать эти окончания
use Path::Tiny; # to read a file conveniently
my %lu = map { $_ => 1 } path($excl_file)->lines({ chomp => 1 });
while (<>) {
chomp;
# [^\w-] protects hyphenated words (or just use \W)
# Or: s{(/.+$}{}g; if "/" is the only possibility
s/([^\w-].+)$//g;
next if exists $lu{$_};
$_ .= $1 if $1;
say;
}
Это будет намного эффективнее для больших словарей и длинных списков исключаемых слов.
Однако это гораздо сложнее и, вероятно, не соответствует некоторым (неустановленным) требованиям.
Используя grep, сопоставляющий целые слова:
grep -wvf exclude.txt en.txt
Объяснение (от man grep)
-w
--word-regexp
Выберите только те строки, которые содержат совпадения, образующие целые слова.-v
--invert-match
Инвертируйте смысл совпадения, чтобы выбрать несовпадающие строки.-f
-f FILE
Получить шаблоны из ФАЙЛА, по одному в строке.Вывод
testing
access/p
batches
cross
попробовал параметр -w. Но это не дает правильных результатов, потому что мой файл содержит символы Юникода, а grep не полностью совместим. Но rg (ripgrep), похоже, работает как положено. Я не упомянул о нелатинских символах, когда спрашивал, потому что не думал, что это будет иметь какое-то значение.
grep -w
, кажется, требуется совпадение целых слов, вы пробовали это?