特定の文字列の検索と構造全体の削除

特定の文字列の検索と構造全体の削除

各単語(タグ)が4列の別々の行にある垂直ファイルがあります。次のように文書化されたメタ構造もあります<doc><s>

<doc name="sth" url="http">
<p>
<s>
Here   here   k1gInSc1   here
is   be   k1gMnPc2   be
a  a   k2eAgMnPc1d1   a
sentence   sentence   k1gMnPc1   sentence
<g/>
.       .       kIx.
</s>
</p>
</doc>

問題は時々文字エンコーディングが間違っていることです。またはたとえば、最初の列では

<doc name="sth" url="http">
<p>
<s>
Here   here   k1gInSc1   here
is   be   k1gMnPc2   be
Ă  Ă   k?   Ă
sentence   sentence   k1gMnPc1   sentence
<g/>
.       .       kIx.
</s>
</p>
</doc>

これらの文字を見つけて文書全体の構造を削除する必要があります。だから私が見つけたら<doc...>1行からすべての行間のすべての項目を削除する必要があります</doc>

私のファイルには10億行があり、そのうち約1000行に誤ってエンコードされた文字が含まれています。

私はgrepを使って間違った文字を見つけます。

xzcat file.vert.xz | grep -i "Ă\|Ĺ\|ľ\|ş\|Ä" > file_bad_characters.txt

どのようにこれらの文字を検出し、その行だけでなく、<doc>構造間のテキスト全体を削除できますか?

答え1

正しいアプローチは、適切なXMLパーサーを使用することです。ただし、この場合、次の方法を使用すると回避策になる可能性があります。

  1. ファイルから空白行をすべて削除します。

    sed -i '/^\s*$/d' file
    
  2. 各項目の前に空白行を追加します<doc>

    sed -i 's/<doc/\n\n<doc/' file 
    
  3. 「行」が「段落」(空白行の前のテキスト部分)として定義されているユーザーPerlの「段落モード」:

    perl -00 -ne 'print unless /[ĂĹľşÄ]/' file > newfile
    

    または、ソースファイルを置き換えるには、次の手順を実行します。

    perl -i.bak -00 -ne 'print unless /[ĂĹľşÄ]/' file
    

重要<doc...:すべてがタグ内にあるよく構造化されたファイルを想定しています。

答え2

残念ながら、これはgrepだけでは実行できないことです。 grepが提供できない行の一部のコンテキストを維持する必要があります。ただし、これを実行できる他の多くの言語があります。例は次のとおりですawk

awk '/<doc>/ {text=""; output=1}
     /Ă|Ĺ|ľ|ş|Ä/{output=0}
     {text = text $0 "\n"}
     /<\/doc>/ {if(output==1){printf "%s", text}}"

これはバッファを生成し、text入力でトークンを表示すると呼び出され、印刷するテキストを表すフラグを設定します。<doc>禁止されている文字に会うとフラグがクリアされます。 token に会うと</doc>フラグがまだ設定されていることを確認し、そうであればバッファを表示します。最後に、フラグが設定されているかどうかに関係なく、各行がバッファに追加されます。

関連情報