次の文字列があります。
one one tow tow three three tow one three
重複した単語を削除して次のように作成するにはどうすればよいですか?
one tow three tow one three
重要なのは、隣接する重複語だけを削除するスクリプトを作成したいということです。
私は試した:
echo "$string" | awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf("%s%s",$i,FS)}{printf("\n")}'
ただし、隣接していない重複語も削除します。
答え1
複数文字RS
と\s
速記にGNU awkを使用する:
$ echo 'one one tow tow three three tow one three' |
awk -v RS='\\s+' '
$0 != prev { out = (NR>1 ? out OFS : "") $0; prev = $0 }
END { print out }
'
one tow three tow one three
またはまだGNU awkですが、以下からインスピレーションを受けました。@nezabudkaの返信ただし、入力フィールドを区切るスペースの順序と入力フィールドに含まれる文字に関係なく正しく機能し、出力が終了するようにいくつかの変更が行われたため、\n
有効なPOSIXテキストです。文書:
$ echo one one tow tow three three tow one three |
awk -v RS='[[:blank:]]+' '
$1 != prev { out = out $1 RT; prev=$1 }
END { print out }
'
one tow three tow one three
それ以外の場合は awk を使用してください。
$ echo 'one one tow tow three three tow one three' |
awk '{
out = $1
for ( i=2; i<=NF; i++ ) {
if ( $i != $(i-1) ) {
out = out OFS $i
}
}
print out
}'
one tow three tow one three
答え2
行に2500個(例:1000個)以下の列がある場合:
echo one one tow tow three three tow one three |
fmt -1 | uniq | fmt -1000
GNU awk:
echo one one tow tow three three tow one three |
awk -v RS=' ' '$1 != D {printf "%s", $1 (RT?RS:ORS); D=$1}'
更新(改行で終わることが確実な場合):
echo one one tow tow three three tow one three |
awk -v RS='[[:space:]]' '$1 != D {printf "%s", $1 RT; D=$1}'
そうでない場合(一般的な方法):
echo -n one one tow tow three three tow one three |
awk -v RS='[[:space:]]' '$1 != D {printf "%s", $1 (RT?RT:ORS); D=$1}'
注:
GNUバージョンには、RSのテンプレートに対応する実際の値が割り当てられる組み込み変数RTがあります。たとえば、[[:space:]]
RS変数にテンプレートが定義されている場合、RT変数には各ケース(スペース、タブ、または改行)でレコードを終了する文字が動的に割り当てられます。 RS変数に文字クラステンプレートが割り当てられている場合は、三項RS=[[:space:]]
演算子を(RT?RT:ORS)
またはに変更する必要があります。RT
答え3
uniq
別の行の後にすべての単語を入れるには、次のようにします。
string='one one tow tow three three tow one three'
printf '%s\n' "${string// /
}" | uniq | paste -sd ' ' -
またはを使用してperl
複数の空白文字を許可して単語を区切り、繰り返しグループ間の空白を保持します。
string=' one one tow tow three three tow one three '
perl -le 'print s/(?<!\S)(\S+)(\s+\1)+(?!\S)/\1/gr for @ARGV' -- "$string"
以下を提供します。
one tow three tow one three
ksh93の${var//pattern/replacement}
パラメータ拡張演算子と同じです(bashを含む他の一部のシェルはこの演算子をコピーしましたが、高度なパターン一致演算子はコピーしませんでした)。
$ string=' one one tow tow three three tow one three '
$ print -r - "${string//~(<!\S)+(\S)+(+(\s)\1)~(!\S)/\1}"
one tow three tow one three
またはzsh
(他のシェルはPerlに似たパターンマッチング演算子をサポートしています)を使用してその変数を変更します。
$ string=' one one tow tow three three tow one three '
$ autoload regexp-replace
$ set -o rematchpcre
$ regexp-replace string '(?<!\S)(\S+)(\s+\1)+(?!\S)' '$match[1]'
$ print -r - "$string"
one tow three tow one three
またはfish
:
$ set string ' one one tow tow three three tow one three '
$ string replace -a --regex '(?<!\S)(\S+)(\s+\1)+(?!\S)' '$1' $string
one tow three tow one three
例の単語がすべて数字(またはアンダースコア)で構成されている場合は、ビジボックスの実装と同様のアプローチを取ることができます。awk
ここで、負の検索perl演算子は、\<
および\>
単語境界演算子に置き換えることができます(perlに似ているため、/\b
に近い)。 Perlツアー演算子として):(?<!\w)
(?!\w)
$ printf '%s\n' "$string" | busybox awk '{print gensub("\\<(\\S+)(\\s+\\1)+\\>", "\\1", "g")}'
one tow three tow one three
単語に数字や下線以外の文字が含まれている場合、この方法は使用できません。たとえば、 と の間に単語の境界があるone-two two three
ため、 に変更されます。one-two three
-
two
答え4
パールを使用してください。たとえば、次は行の境界を超えて隣接する重複する単語を削除することもできます(perlの-0777
オプションを使用して入力全体を一度に吸収します)。
$ printf 'one one two\n two two\ntwo three three two\none\nthree\nthree\n' |
perl -0777 -p -e 's/\b(\w+)(?:\s+\1)+\b/$1/g'
one two three two
one
three
\1
ジョブの左側(LHS)は、s/search (LHS)/replace (RHS)/
以前に一致したパターングループの逆参照です(\w+)
。 $1
置換ジョブまたはジョブの右側にある同じキャプチャグループ。
しかし、これをPerlに入力しないと、入力は次のようになります。複数行には繰り返される隣接する単語が含まれています。
$ printf 'one one two\n two two\ntwo three three two\none\nthree\nthree\n'
one one two
two two
two three three two
one
three
three
メモ:
\b
^
orに似たアンカーですが、$
行の先頭または末尾を一致させるのではなく、単語間の(幅が0の)境界と一致します。\w
マニュアルページで、次のように定義されているすべての単語文字と一致しますperlre
。
\w[3] は「単語」文字と一致します(英数字と「_」、その他の接続句読点、Unicode マーカー)。
...
[3] 詳細については、perlunicode の「Unicode 文字属性」を参照してください。
アルファベット(アルファベットなど)の文字(数字や下線を除く)のみを厳密に一致させる場合は、代わりに[[:alpha:]]+
使用できます\w+
。
- 入力テキストにUnicode文字が含まれている場合、これを処理する方法はいくつかありますが、最も簡単な方法はPerlの
-C
オプションを使用することです。
$ echo 'öne öne öne two öne one' |
perl -C -0777 -p -e 's/\b([[:alpha:]]+)(?:\s+\1)+\b/$1/g'
öne two öne one
このオプションの詳細を表示してman perlrun
検索してください。-C
このトピックに本当に興味がある場合は、およびperlunicode
のperlunitut
マニュアルperluniintro
ページも参照してくださいperlunifaq
。広範な文書で推測できるように、Unicodeを扱うことはほとんどの場合簡単で簡単ですが、さまざまな状況では非常に複雑で微妙です。