Удалить последовательные повторяющиеся слова из файла, используя awk или sed - PullRequest
0 голосов
/ 21 января 2020

Мой входной файл выглядит следующим образом:

“true true, rohith Rohith;
cold burn, and fact and fact good good?”

Вывод должен выглядеть примерно так:

"true, rohith Rohith;
cold burn, and fact and fact good?"

Я пытаюсь сделать то же самое с awk, но не могу получить желаемый результат. результат.

awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf("%s ",$i,FS)}{printf("\n")}' input.txt

Может кто-нибудь, пожалуйста, помогите мне здесь.

С уважением, Рохит

Ответы [ 6 ]

5 голосов
/ 21 января 2020

С GNU awk для 4-го аргумента для разделения ():

$ cat tst.awk
{
    n = split($0,words,/[^[:alpha:]]+/,seps)
    prev = ""
    for (i=1; i<=n; i++) {
        word = words[i]
        if (word != prev) {
            printf "%s%s", seps[i-1], word
        }
        prev = word
    }
    print ""
}

$ awk -f tst.awk file
“true, rohith Rohith;
cold burn, and fact and fact good?”
3 голосов
/ 21 января 2020

Просто сопоставьте ту же обратную ссылку в sed:

sed ':l; s/\(^\|[^[:alpha:]]\)\([[:alpha:]]\{1,\}\)[^[:alpha:]]\{1,\}\2\($\|[^[:alpha:]]\)/\1\2\3/g; tl'

Как это работает:

  • :l - создайте метку l для перехода. См. tl ниже.
  • s - замена
    • /
    • \(^\|[^[:alpha:]]\) - совпадение начала строки или не алфавитного c символа. Это делается для того, чтобы следующая часть соответствовала всему слову, а не только суффиксу.
    • \([[:alpha:]]\{1,\}\) - соответствует слову - один или несколько букв c символов.
    • [^[:alpha:]]\{1,\} - соответствует не-слово - один или несколько не алфавитных c символов.
    • \2 - совпадают с тем же, что и во втором \(...\) - ie. соответствует слову.
    • \($\|[^[:alpha:]]\) - соответствует концу строки или соответствует не алфавиту c символа. Таким образом, мы сопоставляем все второе слово, а не только его префикс.
    • /
    • \1\2\3 - замените его на <beginning of the line or non-alphabetic prefix character><the word><end of the line or non-alphabetic suffix character found>
    • /
    • g - заменить глобально. Но поскольку регулярное выражение никогда не возвращается назад, оно будет заменять 2 слова за раз.
  • tl - Перейти к метке l, если последняя команда s была успешной. Это здесь, так что когда есть 3 одинаковых слова, например true true true, они должным образом заменяются одним true.

Без \(^\|[^[:alpha:]]\) и \($\|[^[:alpha:]]\), без их, например, true rue будет заменено на true, потому что суффикс rue rue будет соответствовать.

Ниже приведены другие мои решения, которые также удаляют повторяющиеся слова в строках.

My Первое решение было с uniq. Поэтому сначала я преобразую ввод в пары в формате <non-alphabetical sequence separating words encoded in hex> <a word>. Затем запустите его через uniq -f1, игнорируя первое поле, а затем выполните обратное преобразование. Это будет очень медленно:

# recreate input
cat <<EOF |
true true, rohith Rohith;
cold burn, and fact and fact good good?
EOF
# insert zero byte after each word and non-word
# the -z option is from GNU sed
sed -r -z 's/[[:alpha:]]+/\x00&\x00/g' |
# for each pair (non-word, word)
xargs -0 -n2 sh -c '
    # ouptut hexadecimal representation of non-word
    printf "%s" "$1" | xxd -p | tr -d "\n"
    # and output space with the word
    printf " %s\n" "$2"
' -- |
# uniq ignores empty fields - so make sure field1 always has something
sed 's/^/-/' |
# uniq while ignoring first field
uniq -f1 |
# for each pair (non-word in hex, word)
xargs -n2 bash -c '
    # just `printf "%s" "$1" | sed 's/^-//' | xxd -r -p` for posix shell
    # change non-word from hex to characters
    printf "%s" "${1:1}" | xxd -r -p
    # output word
    printf "%s" "$2"
' --

Но затем я заметил, что sed хорошо справляется с токенизацией ввода - он помещает нулевые байты между каждым словом и несловесными токенами. Так что я мог легко читать поток. Я могу игнорировать повторяющиеся слова в awk, прочитав разделенный нулями поток в GNU awk и сравнив последнее прочитанное слово:

cat <<EOF |
true true, rohith Rohith;
cold burn, and fact and fact good good?
EOF
sed -r -z 's/[[:alpha:]]+/\x00&\x00/g' |
gawk -vRS='\0' '
NR%2==1{
    nonword=$0
}
NR%2==0{
    if (length(lastword) && lastword != $0) {
        printf "%s%s", lastword, nonword
    }
    lastword=$0
}
END{
    printf "%s%s", lastword, nonword
}'

Вместо нулевого байта в качестве разделителя записей можно использовать что-то уникальное, например ^ символ, таким образом, его можно использовать с не-GNU awk-версией, протестированной с mawk, доступной в repl. Сократил скрипт, используя здесь более короткие имена переменных:

cat <<EOF |
true true, rohith Rohith;
cold burn, and fact and fact good good?
EOF
sed -r 's/[[:alpha:]]+/^&^/g' |
awk -vRS='^' '
    NR%2{ n=$0 }
    NR%2-1 && length(l) && l != $0 { printf "%s%s", l, n }
    NR%2-1 { l=$0 }
    END { printf "%s%s", l, n }
'

Проверено на repl . Вывод фрагментов:

true, rohith Rohith;
cold burn, and fact and fact good?
1 голос
/ 21 января 2020

Простой sed:

echo "true true, rohith Rohith;
cold burn, and fact and fact good good?" | sed -r 's/(\w+) (\1)/\1/g'
1 голос
/ 21 января 2020

Это не совсем то, что вы показали в выводе, но близко, используя gnu-awk:

awk -v RS='[^-_[:alnum:]]+' '$1 == p{printf "%s", RT; next} {p=$1; ORS=RT} 1' file

“true , rohith Rohith;
cold burn, and fact and fact good ?”
0 голосов
/ 21 января 2020
sed -E 's/(\w+) *\1/\1/g' sample.txt

sample.txt

“true true, rohith Rohith;
cold burn, and fact and fact good good?”

вывод:

:~$ sed -E 's/(\w+) *\1/\1/g' sample.txt
“true, rohith Rohith;
cold burn, and fact and fact good?”

Пояснение

(\w) *\1 - соответствует разделенному слову пробелом того же слова и сохраняет его

0 голосов
/ 21 января 2020

В зависимости от ожидаемого ввода, это может работать:

sed -r 's/([a-zA-Z0-9_-]+)( *)\1/\1\2/g ; s/ ([.,;:])/\1/g ; s/  / /g' myfile

([a-zA-Z0-9 _-] +) = слова, которые могут повторяться.

(*) \ 1 = проверить, повторяется ли предыдущее слово после пробела.

s / ([.,;:]) / \ 1 / g = удаляет лишние пробелы перед пунктуацией (возможно, вы захотите добавить символы в эту группу).

s / / / g = удаляет двойные пробелы.

Это работает с GNU sed.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...