無効な(非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には他のソリューションがありますか?
ロケールをUTF-8に設定していると仮定すると、これは無効なUTF-8シーケンスを認識するのにうまく機能します。
grep -axv '.*' file.txt
説明:
したがって、出力があります。これは、行を含むutf8バイトシーケンスではなく無効な行を含む行です(-vが反転しているため)
非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文字を削除します。 。
あなたが見ているのは、定義により破損しています。どうやら、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データを取得する必要があります。
シェルから非ASCII文字を見つけるために、これを試してください。
コマンド:
$ Perl -ne 'print "$. $_" if m/[\x80-\xFF]/' utf8.txt
出力:
2 Pour être ou ne pas être
4 Byť či nebyť
5 是或不
この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///
。
これがお役に立てば幸いです!これがあなたが探していたものではなかったら教えてください。
私はおそらく他の人がすでに言ったことを繰り返しています。しかし、 invalid 文字は有効な可能性があるため、まだ印刷されると思います。 Universal Character Set は、世界中で頻繁に使用される文字を参照して、特殊な文字セットに依存しない堅牢なソフトウェアを作成できるようにする試みです。
だから私はあなたの問題は次の両方のいずれかであると思う-あなたの全体的な目標は一般的にutfファイルからのこの(悪意のある)入力を処理することであると仮定して:
私の意見では、これを処理する2つの可能な方法があります:
iconv -f utf-8 -t ascii -o file_in_ascii.txt file_in_utf8.txt
。ただし、 careful を使用すると、より広い文字スペース(utf)からより小さな文字スペースに移動すると、データが失われる可能性があります。Utfの処理は難しいように思えるかもしれませんが、次の手順はutf対応を実現するのに役立ちます。
uconv
を使用すると、無効なシーケンスのコールバックハンドラを設定できます。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}
次の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;
}