次の形式の行を含む長いテキストファイル(stardict-editorのタブファイル)があります。
word1 some text
word1 some other text
word2 more text
word3 even more
そしてそれを次に変換したいです。
word1 some text<br>some other text
word2 more text
word3 even more
これは、同じ単語(ファイルがソートされている)で始まる後続の行を単一の行(ここではdelimitedと定義されている<br>
)にマージする必要があることを意味します。同じ開始行が複数回表示されることもあります。単語と定義を区別する文字はタブ文字であり、各行で一意です。word1
、、word2
はword3
もちろん、私が事前に知らない任意のコンテンツ(タブと改行を除く)のプレースホルダーです。
これを行うには、より長いPerlコードを考えることができますが、Perlまたはコマンドラインに簡単な解決策があるかどうかを知りたいです。どんなアイデアがありますか?
答え1
perl -p0E 'while(s/^((.+?)\t.*)\n\2\t/$1<br>/gm){}'
(6年後の私のラップトップでは、150万行の23MB辞書を処理するのに2秒かかります。)
答え2
これは標準的な手順です。awk
awk '
{
k=$2
for (i=3;i<=NF;i++)
k=k " " $i
if (! a[$1])
a[$1]=k
else
a[$1]=a[$1] "<br>" k
}
END{
for (i in a)
print i "\t" a[i]
}' long.text.file
ファイルが行の最初の単語にソートされると、スクリプトがより簡単になる可能性があります。
awk '
{
if($1==k)
printf("%s","<br>")
else {
if(NR!=1)
print ""
printf("%s\t",$1)
}
for(i=2;i<NF;i++)
printf("%s ",$i)
printf("%s",$NF)
k=$1
}
END{
print ""
}' long.text.file
そうでなければbash
unset n
while read -r word definition
do
if [ "$last" = "$word" ]
then
printf "<br>%s" "$definition"
else
if [ "$n" ]
then
echo
else
n=1
fi
printf "%s\t%s" "$word" "$definition"
last="$word"
fi
done < long.text.file
echo
答え3
これは実際には標準ですawk
。作業データを変更しないきれいな解決策は次のとおりです。
awk 'BEGIN { FS="\t" }
$1!=key { if (key!="") print out ; key=$1 ; out=$0 ; next }
{ out=out"<br>"$2 }
END { print out }'
答え4
Pythonでは:
import sys
def join(file_name, join_text):
prefix = None
current_line = ''
for line in open(file_name):
if line and line[-1] == '\n':
line = line[:-1]
try:
first_word, rest = line.split('\t', 1)
except:
first_word = None # empty line or one without tab
rest = line
if first_word == prefix:
current_line += join_text + rest
else:
if current_line:
print current_line
current_line = line
prefix = first_word
if current_line: # do the last line(s)
print current_line
join(sys.argv[2], sys.argv[1])
<br>
これには、プログラムの最初の引数として区切り文字()が必要になり、2番目の引数としてファイル名が必要です。