web-dev-qa-db-ja.com

テキストファイルで無効なutf8 unicode / binaryを検出する方法

無効な(非ASCII)utf-8、Unicodeまたはバイナリ文字がある破損したテキストファイルを検出する必要があります。

�>t�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½o��������ï¿ï¿½_��������������������o����������������������￿����ß����������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~�ï¿ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½}���������}w��׿��������������������������������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~������������������������������������_������������������������������������������������������������������������������^����ï¿ï¿½s�����������������������������?�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½}����������ï¿ï¿½ï¿½ï¿½ï¿½y����������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½o�������������������������}��

私が試したもの:

iconv -f utf-8 -t utf-8 -c file.csv 

これにより、ファイルがutf-8エンコードからutf-8エンコードに変換され、-cは、無効なutf-8文字をスキップするためのものです。しかし、それらの違法文字は最後にはまだ印刷されていました。 Linuxまたは他の言語のbashには他のソリューションがありますか?

41
user121196

ロケールをUTF-8に設定していると仮定すると、これは無効なUTF-8シーケンスを認識するのにうまく機能します。

grep -axv '.*' file.txt

説明:

  • -a:ファイルをテキストとして扱い、無効なバイトシーケンスを検出するとgrepが中断しないようにします(utf8ではありません)
  • -v:一致しない行を示す出力を反転します
  • -x '。*':utf8文字で構成される完全な行に一致することを意味します。

したがって、出力があります。これは、行を含むutf8バイトシーケンスではなく無効な行を含む行です(-vが反転しているため)

50
Blaf

非ASCII=文字の場合はgrepになります。

GNU grep with pcre(-P、常に利用できるわけではありません。 FreeBSDでは、パッケージpcre2でpcregrepを使用できます。

grep -P "[\x80-\xFF]" file

のリファレンスUNIXのすべての非ASCII文字に対してgrepを実行するには 。そのため、実際には、ファイルにASCII文字以外の文字が含まれているかどうかを確認するだけの場合、次のように言えます。

if grep -qP "[\x80-\xFF]" file ; then echo "file contains ascii"; fi
#        ^
#        silent grep

これらの文字を削除するには、次を使用できます。

sed -i.bak 's/[\d128-\d255]//g' file

これにより、file.bakファイルをバックアップとして、元のfileは非ASCII文字が削除されます。 csvから非ASCII文字を削除します。

12
fedorqui

あなたが見ているのは、定義により破損しています。どうやら、Latin-1でレンダリングされたファイルを表示しているようです。 3文字は、3バイト値0xEF 0xBF 0xBDを表します。しかし、それらはUnicodeのUTF-8エンコーディングです REPLACEMENT CHARACTER U + FFFD これは、不明または未定義のエンコーディングからバイトをUTF-8に変換しようとした結果であり、次のように適切に表示されます。 �(今世紀のブラウザをお持ちの場合、疑問符が入った黒いひし形のようなものが表示されるはずですが、これは使用しているフォントなどにも依存します).

したがって、この特定の現象を「検出する方法」についての質問は簡単です。 UnicodeコードポイントU + FFFDは完全に無料であり、あなたが示唆しているプロセスから考えられる唯一の症状です。

これらは、有効なUnicodeコードポイントをエンコードする有効なUTF-8シーケンスであるという意味で、「無効なUnicode」または「無効なUTF-8」ではありません。この特定のコードポイントのセマンティクスが「これは適切に表現できなかった文字の置換文字である」、つまり無効な入力であるだけです。

そもそもそれを防ぐ方法については、答えは非常に単純ですが、むしろ情報価値がありません。誤ったエンコーディングがいつどのように発生したかを特定し、この無効な出力を生成したプロセスを修正する必要があります。

U + FFFD文字を削除するには、次のようにします

Perl -CSD -pe 's/\x{FFFD}//g' file

繰り返しますが、適切な解決策は、そもそもこれらの誤った出力を生成しないことです。

(サンプルデータのエンコードを公開していません。追加の破損がある可能性があります。表示されているのがコピー/データのUTF-8レンダリングの貼り付け、「二重エンコード」されています。つまり、誰かが上記のように既に破損しているUTF-8テキストを取得し、コンピューターにラテン語から変換するように指示しました-1からUTF-8へ。簡単に元に戻すことができます;それをLatin-1に「戻す」だけで変換できます。この場合、余分な誤った変換を行う前に、元のUTF-8データを取得する必要があります。

4
tripleee

シェルから非ASCII文字を見つけるために、これを試してください。

コマンド:

$ Perl -ne 'print "$. $_" if m/[\x80-\xFF]/'  utf8.txt

出力:

2 Pour être ou ne pas être
4 Byť či nebyť
5 是或不
3
Bouramas

このPerlプログラムは、非ASCII文字をすべて削除する必要があります。

 foreach $file (@ARGV) {
   open(IN, $file);
   open(OUT, "> super-temporary-utf8-replacement-file-which-should-never-be-used-EVER");
   while (<IN>) {
     s/[^[:ascii:]]//g;
     print OUT "$_";
   }
   rename "super-temporary-utf8-replacement-file-which-should-never-be-used-EVER", $file;
}

これが行うのは、次のようにコマンドラインで入力としてファイルを取得することです。
Perl fixutf8.pl foo bar baz
次に、各行で、非ASCII文字の各インスタンスを何も置き換えません(削除)。
次に、この変更された行をsuper-temporary-utf8-replacement-file-which-should-never-be-used-EVER(他のファイルを変更しないように名前が付けられています。)
その後、一時ファイルの名前を元のファイルに変更します。

これは、特殊な用途がある場合に、ALL ASCII文字(DEL、NUL、CRなどを含む)を受け入れます。印刷可能な文字のみが必要な場合は、単に置き換えます:ascii: with :print: in s///

これがお役に立てば幸いです!これがあなたが探していたものではなかったら教えてください。

3
ASCIIThenANSI

私はおそらく他の人がすでに言ったことを繰り返しています。しかし、 invalid 文字は有効な可能性があるため、まだ印刷されると思います。 Universal Character Set は、世界中で頻繁に使用される文字を参照して、特殊な文字セットに依存しない堅牢なソフトウェアを作成できるようにする試みです。

だから私はあなたの問題は次の両方のいずれかであると思う-あなたの全体的な目標は一般的にutfファイルからのこの(悪意のある)入力を処理することであると仮定して:

  1. 無効なutf8文字があります(無効なバイトシーケンスと呼ばれる方が良い-このため、対応する Wikipedia-Article を参照したい) 。
  2. 現在の表示フォントにはなし同等物があり、特別な記号に置き換えられるか、バイナリASCII同等物として表示されます(fe-iしたがって、次のso-postを参照したい: TF-8の特殊文字は表示されません )。

私の意見では、これを処理する2つの可能な方法があります:

  1. 変換すべての文字utf8から処理可能なものへ-f.e.[〜#〜] ascii [〜#〜]-これはf.e. iconv -f utf-8 -t ascii -o file_in_ascii.txt file_in_utf8.txt。ただし、 careful を使用すると、より広い文字スペース(utf)からより小さな文字スペースに移動すると、データが失われる可能性があります。
  2. utf(8)を正しく処理します-これは世界が何かを書いている方法です。後処理ステップに制限があるためにASCII文字に依存する必要があると思われる場合は、停止して再考してください。ほとんどの場合、ポストプロセッサはすでにutfをサポートしています。おそらく、それを利用する方法を見つける方が良いでしょう。あなたは自分のものを将来性と防弾性を備えています。

Utfの処理は難しいように思えるかもしれませんが、次の手順はutf対応を実現するのに役立ちます。

  • Utfを正しく表示できるか、ディスプレイスタック(OS、端末など)がUnicodeの適切なサブセット(もちろん、ニーズを満たす必要がある)を表示できることを確認してください。これにより、16進数の必要性を防ぐことができます。 -多くの場合、エディタ。残念なことに、utfは大きすぎて1つのフォントに入れることはできませんが、開始するのに適したポイントは次のポストです: https://stackoverflow.com/questions/586503/complete-monospaced-unicode-font
  • 無効なバイトシーケンスをフィルタリングできます。そして、それを達成するための多くの方法があります。このul-postはこれらの方法の多くを示しています: Filtering invalid utf8 - uconv を使用すると、無効なシーケンスのコールバックハンドラを設定できます。
  • Unicodeについてもう少し読んでください。
1
Florian Breisch

python 3の非常に汚い解決策

import sys
with open ("cur.txt","r",encoding="utf-8") as f:
    for i in f:
            for c in i:
                 if(ord(c)<128):
                     print(c,end="")

出力は次のようになります。

>two_o~}}w~_^s?w}yo}
1
xitij

次のCプログラムは、無効なutf8文字を検出します。 Linuxシステムでテストおよび使用されました。

/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>

void usage( void ) {
    printf( "Usage: test_utf8 file ...\n" );

    return;
}

int line_number = 1;
int char_number = 1;
char *file_name = NULL;

void inv_char( void ) {
    printf( "%s: line : %d - char %d\n", file_name, line_number, char_number );

    return;
}

int main( int argc, char *argv[]) {

    FILE *out = NULL;
    FILE *fh = NULL;

//    printf( "argc: %d\n", argc );

    if( argc < 2 ) {
        usage();
        exit( 1 );
    }

//    printf( "File: %s\n", argv[1] );

    file_name = argv[1];

    fh = fopen( file_name, "rb" );
    if( ! fh ) {
        printf( "Could not open file '%s'\n", file_name );
        exit( 1 );
    }

    int utf8_type = 1;
    int utf8_1 = 0;
    int utf8_2 = 0;
    int utf8_3 = 0;
    int utf8_4 = 0;
    int byte_count = 0;
    int expected_byte_count = 0;

    int cin = fgetc( fh );
    while( ! feof( fh ) ) {
        switch( utf8_type ) {
            case 1:
                if( (cin & 0x80) ) {
                    if( (cin & 0xe0) == 0xc0 ) {
                        utf8_1 = cin;
                        utf8_type = 2;
                        byte_count = 1;
                        expected_byte_count = 2;
                        break;
                    }

                    if( (cin & 0xf0) == 0xe0 ) {
                        utf8_1 = cin;
                        utf8_type = 2;
                        byte_count = 1;
                        expected_byte_count = 3;
                        break;
                    }

                    if( (cin & 0xf8) == 0xf0 ) {
                        utf8_1 = cin;
                        utf8_type = 2;
                        byte_count = 1;
                        expected_byte_count = 4;
                        break;
                    }

                    inv_char();
                    utf8_type = 1;
                    break;
                }

                break;

            case 2:
            case 3:
            case 4:
//                printf( "utf8_type - %d\n", utf8_type );
//                printf( "%c - %02x\n", cin, cin );
                if( (cin & 0xc0) == 0x80 ) {
                    if( utf8_type == expected_byte_count ) {
                        utf8_type = 1;
                        break;
                    }

                    byte_count = utf8_type;
                    utf8_type++;

                    if( utf8_type == 5 ) {
                        utf8_type = 1;
                    }

                    break;
                }

                inv_char();
                utf8_type = 1;
                break;

            default:
                inv_char();
                utf8_type = 1;
                break;
        }

        if( cin == '\n' ) {
            line_number ++;
            char_number = 0;
        }

        if( out != NULL ) {
            fputc( cin, out );
        }

//        printf( "lno: %d\n", line_number );

        cin = fgetc( fh );
        char_number++;
    }

    fclose( fh );

    return 0;
}
1
DBA