Извлечь текст с разными разделителями

мой текстовый файл выглядит так

foo.en 14 :: xyz 1;foo bar 2;foofoo 5;bar 9
bar.es 18 :: foo bar 4;kjp bar 2;bar 6;barbar 8

Игнорирование текста перед разделителем ::, существует ли однострочная команда unix (разрешено много каналов) или один лайнер-скрипт Perl, которые извлекают текст таким образом, что на выходе выводятся уникальные слова, разделенные ;?:

xyz
foo bar
foofoo
bar
kjp bar
barbar

Я пробовал перебирать текстовый файл с помощью скрипта Python, но я ищу однострочник для этой задачи.

ans = set()
for line in open(textfile):
  ans.add(line.partition(" :: ")[1].split(";").split(" ")[:-1])

for a in ans:
  print a

person alvas    schedule 27.02.2013    source источник
comment
Я сделал это с помощью Python и перебирал текстовый файл. Я просто ищу способ попроще.   -  person alvas    schedule 27.02.2013


Ответы (4)


С Perl:

perl -nle 's/.*?::\s*//;!$s{$_}++ and print for split /\s*\d+;?/' input

Описание:

s/.*?::\s*//;  # delete up to the first '::'

Эта часть:

!$s{$_}++ and print for split /\s*\d+;?/

можно переписать так:

foreach my $word (split /\s*\d+;?/) {   # for split /\s*\d+;?/
  if (not defined $seen{$word}}) {      # !$s{$_}
    print $word;                        # and print
  }
  $seen{$word}++;                       # $s{$_}++
}

Поскольку приращение в !$s{$_}++ является пост-приращением, Perl сначала проверяет ложное условие, а затем выполняет приращение. Неопределенное хеш-значение имеет значение 0. Если тест не прошел, т. Е. $s{$_} был ранее увеличен, то части и пропускаются из-за короткое замыкание.

person perreal    schedule 27.02.2013
comment
Насколько я люблю Perl, @Floris получил награду за трубку. Не волнуйся, ты второй на месте, если я позволю 2 ответа =) - person alvas; 27.02.2013
comment
:) просто сравните выходы - person perreal; 27.02.2013
comment
все они дали одинаковый результат. @perreal не получит награду без трубок, лолз - person alvas; 27.02.2013
comment
Я бы, вероятно, проголосовал за этот ответ, если бы мог понять, как он работает ... но никаких объяснений не было. !$s{$_}++ and print for split ??? !!! Я уверен, что это умно - я тоже хотел бы быть таким. - person Floris; 28.02.2013
comment
@ Флорис, я добавил описание, надеюсь, этого достаточно. - person perreal; 28.02.2013
comment
Да, конечно. Вы получили +1! - person Floris; 28.02.2013

cat textfile | sed 's/.*:://g' |  tr '[0-9]*;' '\n' | sort -u

Объяснение:

sed 's/.*:://g'      Take everything up to and including `::` and replace it with nothing
tr '[0-9];' '\n'     Replace numbers and semicolon with newlines
sort -u              Sort, and return unique instances

я считаю, что это приводит к отсортированному результату ...

person Floris    schedule 27.02.2013
comment
также проблема в том, что foo bar не будет выводиться, он будет выводить foo и bar для foo bar - person alvas; 27.02.2013
comment
@ 2er0 - вы правы по обоим пунктам - Спасибо! Я видел, как вы редактировали в то же самое время, когда я редактировал, чтобы исправить ошибку (foo bar) ... Я добавил сортировку и думаю, что теперь она правильная. - person Floris; 27.02.2013
comment
grep -o -E '\(.*\);' не кажется правильным. это должно быть grep -o -E '\(.*\)\;' - person alvas; 27.02.2013
comment
Упростил последний grep и два seds с помощью команды tr и объединил сортировку и uniq. Теперь немного компактнее - person Floris; 27.02.2013
comment
Спасибо @sputnick. Полагаю, это означает, что ты думаешь, что я курю? Или курить что-нибудь? ... - person Floris; 27.02.2013
comment
Я саркастичен. 4 (перед редактированием) unix пайпа делать много =) - person Gilles Quenot; 27.02.2013

Вы можете попробовать это:

$ awk -F ' :: ' '{print $2}' input.txt | grep -oP '[^0-9;]+' | sort -u
bar 
barbar 
foo bar 
foofoo 
kjp bar 
xyz 

Если ваши фразы содержат числа, попробуйте это регулярное выражение perl: '[^;]+?(?=\s+\d+(;|$))'

person kev    schedule 27.02.2013

Только с awk:

$ awk -F' :: ' '{
    gsub(/[0-9]+/, "")
    split($2, arr, /;/ )
    for (a in arr) arr2[arr[a]]=""
}
END{
    for (i in arr2) print i
}' textfile.txt

И однострочная версия:

 awk -F' :: ' '{gsub(/[0-9]+/, "");split($2, arr, /;/ );for (a in arr) arr2[arr[a]]="";}END{for (i in arr2) print i}' textfile.txt
person Gilles Quenot    schedule 27.02.2013