Задача

Параметр здесь - это имя файла! Файл содержит текст. Задача скрипта — решить, какое слово чаще всего встречается в других словах.


Пример ввода и вывода

(например, текст такой: играть в мяч, футбол, баскетбол, снежок, следовательно, мяч является победителем, потому что он является частью трех других миров).


Мой код софар

Я сделал этот код до сих пор, но он не работает для каждого вывода

!/bin/sh
awk '{for(i=2;i<NF;i++) {s=$i; for(j=i+1;j<=NF;j++) print s=s FS $j}}' $1 | sort | uniq -c | sort -k1,1rn -k2 | sed 's/ *[^ ]* *//;q' | cut -f1 -d" "
1
Community 11 Июн 2020 в 17:16
Самое частотное слово..... как мячик в примере
 – 
user405815
13 Апр 2020 в 00:53
Так вы действительно ищете подстроки? Или вам нужно проверять каждую подстроку по списку слов на случай, если строка не является правильным словом? Строка a встречается чаще, чем ball, а l даже чаще.
 – 
Kusalananda
13 Апр 2020 в 00:55
Нет, если бы я искал подстроки, это было бы так, как вы сказали, но то, что я хочу сравнить между самими словами, большинством слов, которые существуют в других словах, поэтому в основном мы должны дать слово здесь, в примере, слово «мяч». встречаются в футболе, баскетболе и снежке, поэтому мы хотим использовать это слово в качестве результата.
 – 
user405815
13 Апр 2020 в 01:12

2 ответа

Лучший ответ

Если список слов находится в файле words с одним словом в каждой строке (возможно, созданным с помощью tr ' ' '\n' <originalwords >words для разделения исходного списка на несколько строк), то цикл

while IFS= read -r word; do
    grep -F -o -e "$word" words
done <words | awk '{ c[$0]++; if (c[$0] > c[w]) w = $0 } END { print w }'

Выведет слово, которое чаще всего встречается в составе слов в списке (или, если много слов встречается одинаковое количество раз, одно из тех слов, которые встречаются первыми в списке).

Он делает это, используя сам список как набор шаблонов для сопоставления со списком. С помощью -o мы просим возвращать совпадающие подстроки в отдельных строках.

Только вывод цикла со списком, указанным в вопросе, будет

play
ball
ball
ball
ball
football
basketball
snowball

Затем нужно просто подсчитать эти слова и выбрать то, которое встречается чаще всего.


Как полный скрипт с временной обработкой файлов:

#!/bin/sh

tmpfile=$(mktemp)

trap 'rm -f "$tmpfile"' EXIT      # delete temporary file upon exiting

tr -s ' ' '\n' <"${1:-/dev/stdin}" >"$tmpfile"  # convert into word list

while IFS= read -r word; do
    grep -F -o -e "$word" "$tmpfile"
done <"$tmpfile" | awk '{ c[$0]++; if (c[$0] > c[w]) w = $0 } END { print w }'

Скрипт дополнительно читает из стандартного ввода, если файл не указан.

1
Kusalananda 13 Апр 2020 в 09:16
Что, если я хочу ввести только одну строку, это сработает?
 – 
user405815
13 Апр 2020 в 01:34
Вам придется разделить его на строки, возможно, пропустив через tr ' ' '\n'.
 – 
Kusalananda
13 Апр 2020 в 01:35
Где я должен сделать команду tr
 – 
user405815
13 Апр 2020 в 01:46
Тр ' ' '\n' | grep -o -f слова слова | сортировать | уникальный -c | сортировать -n | sed 's/.*[[:digit:]] //; д'
 – 
user405815
13 Апр 2020 в 01:47
Нет. Вам нужно будет создать файл words из исходного файла следующим образом: tr ' ' '\n' <original >words, затем вы сможете запустить конвейер.
 – 
Kusalananda
13 Апр 2020 в 01:50
awk '{
        for (i=1; i<=NF; i++) {
                uwords[$i] = 0
                allwords[++idx] = $i
        }
     }
    END {
                if (idx == 0) exit
                max = 0
                for (w in uwords) {
                        count = 0
                        for (i=1; i<=idx; i++) {
                                if (allwords[i] ~ w) count++;
                        }
                        if (count > max) {
                                max = count
                                maxw = w
                        }
                }
                print maxw
        }'

Просканируйте ввод и извлеките список уникальных слов и список всех слов. (Думаю, нам не нужен список уникальных слов, но это может сделать вещи более эффективными в случае большого ввода.) Затем для каждого уникального слова подсчитайте, сколько слов в файле соответствует ему. (Так, если файл содержит football football football, это означает 3 в сторону ball.) Следите за тем, у кого больше всего совпадений.

В случае ничьей, он сообщает о слове, которое появляется первым в массиве uwords (уникальные слова). Это не обязательно первое, что появляется в файле, и не первый в алфавитном порядке.

Это может привести к неожиданным результатам если какое-либо из слов содержит ., * или [.


Если вы предпочитаете подход Кусалананды shell + awk, но не хотите ошибки пограничного случая, сделайте следующее:

tmpfile=$(mktemp)

trap 'rm -f "$tmpfile"' EXIT      # delete temporary file upon exiting

tr -s ' ' '\n' < "${1:-/dev/stdin}" > "$tmpfile"  # convert into word list

sort -u "$tmpfile" | while IFS= read -r word
do
    grep -F -o -e "$word" "$tmpfile"
done | awk '{ c[$0]++; if (c[$0] > c[w]) w = $0 } END { print w }'

Сортируя список слов, мы получаем список уникальных слов и, таким образом, не считаем слова несколько раз.

Обратите внимание, что этот код явно предполагает что существует не более одного входного файла (но файла может и не быть, т.е. читать со стандартного ввода). Это соответствует формулировке вопроса. Однако, если может быть любое количество входных файлов (ноль, один, или больше), измените строку tr на

cat -- "$@" | tr -s ' ' '\n' > "$tmpfile"         # convert into word list

Возможно, это UUOC, но

  • он обрабатывает случай двух или более входных файлов, и
  • это более читабельно, чем < "${1:-/dev/stdin}".
1
G-Man Says 'Reinstate Monica' 10 Май 2020 в 13:09
Привет, можно было бы использовать индексную функцию index(allwords[i], w) вместо регулярного выражения allwords[i] ~ w
 – 
guest
14 Апр 2020 в 02:50
Да, конечно. Фактически, я рассматривал возможность его использования; Я остановился на ~, потому что это было короче, и я не хотел объяснять, почему я использовал index.
 – 
G-Man Says 'Reinstate Monica'
14 Апр 2020 в 02:59