Как обнаружить недействительный 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. Однако в конце эти нелегальные символы все же были напечатаны. Есть ли другие решения в bash на linux или других языках?
Ответы
Ответ 1
Предполагая, что для вашей локали установлено UTF-8, это хорошо работает для распознавания недопустимых последовательностей UTF-8:
grep -axv '.*' file.txt
Объяснение:
- -a: обрабатывает файл как текст, существенно предотвращает прерывание grep при обнаружении недопустимой последовательности байтов (не являющейся utf8)
- -v: инвертирует выходные данные, показывая несоответствующие строки
- -x '. *': Означает совпадение с полной строкой, состоящей из любого символа utf8.
Следовательно, будет вывод, то есть строки, содержащие недопустимую последовательность байтов not utf8, содержащую строки (так как инвертировано -v)
Ответ 2
Я бы grep
для символов без ASCII.
С GNU grep с pcre (из-за -P
, недоступно всегда). В FreeBSD вы можете использовать pcregrep в пакете pcre2):
grep -P "[\x80-\xFF]" file
Ссылка в Как мне grep Для всех символов, отличных от ASCII, в UNIX. Итак, на самом деле, если вы хотите проверить, содержит ли файл не символы 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. Ссылка в Удалить символы без ascii из csv.
Ответ 3
То, что вы ищете, по определению является искаженным. По-видимому, вы показываете файл, как он отображается на латинице-1; три символа � представляют три байтовых значения 0xEF 0xBF 0xBD. Но это кодировка UTF-8 Unicode REPLACEMENT CHARACTER U + FFFD, которая является результатом попытки конвертировать байты из неизвестного или undefined кодирование в UTF-8 и которое будет правильно отображаться как (если у вас есть браузер из этого столетия, вы должны увидеть что-то вроде черного бриллианта с вопросительным знаком в нем, но это также зависит от того, какой шрифт вы используете и т.д.).
Итак, ваш вопрос о том, "как обнаружить" это конкретное явление, легко; кодовая точка Unicode U + FFFD является мертвой поддачей и единственным возможным симптомом из процесса, который вы подразумеваете.
Это не "недопустимый Unicode" или "недопустимый UTF-8" в том смысле, что это допустимая последовательность UTF-8, которая кодирует действительную кодовую точку Unicode; это просто, что семантика этой конкретной кодовой точки является "это замещающий символ для символа, который не может быть представлен должным образом", т.е. неверный ввод.
Что касается того, как предотвратить это в первую очередь, ответ действительно прост, но также довольно неинформативен - вам нужно определить, когда и как произошло неправильное кодирование, и исправить процесс, который вызвал этот недопустимый вывод.
Чтобы просто удалить символы U + FFFD, попробуйте что-то вроде
perl -CSD -pe 's/\x{FFFD}//g' file
но опять же, правильное решение состоит в том, чтобы не генерировать эти ошибочные выходы в первую очередь.
(Вы не показываете кодировку ваших данных примера. Возможно, что у него есть дополнительное повреждение. Если то, что вы показываете нам, является копией/вставкой данных UTF-8 данных, оно было "двойным кодированием". Иными словами, кто-то взял - уже поврежден, как указано выше - текст UTF-8, и сказал компьютеру перевести его с латинского-1 на UTF-8. Отменить это легко, просто конвертируйте его "назад" на латинский-1. То, что вы получите, должно быть исходными данными UTF-8 перед избыточным неправильным преобразованием.)
Ответ 4
Попробуйте это, чтобы найти не-ASCII символы из оболочки.
Команда:
$ perl -ne 'print "$. $_" if m/[\x80-\xFF]/' utf8.txt
Выход:
2 Pour être ou ne pas être
4 Byť či nebyť
5 是或不
Ответ 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
(назван так, что он не может изменять любые другие файлы.)
После этого он переименовывает временный файл в файл исходного файла.
Он принимает ВСЕ символы ASCII (включая DEL, NUL, CR и т.д.), Если у вас есть для них специальное использование. Если вы хотите только печатные символы, просто замените :ascii:
на :print:
в s///
.
Надеюсь, это поможет! Пожалуйста, дайте мне знать, если это не то, что вы искали.
Ответ 6
Я, вероятно, повторяю то, что уже сказали другие. Но я думаю, что ваши недопустимые символы печатаются, потому что они могут быть действительными. Универсальный набор символов - это попытка ссылаться на всемирно часто используемые символы, чтобы иметь возможность писать надежное программное обеспечение, которое не полагается на специальный набор символов.
Итак, я думаю, что ваша проблема может быть одной из следующих: в предположении, что ваша общая цель состоит в том, чтобы обрабатывать этот (вредоносный) ввод из utf файлов в целом:
- недействительные символы utf8 (лучше называемые неверными последовательностями байтов). Для этого я хотел бы обратиться к соответствующему Wikipedia -Статья).
- В текущем шрифте дисплея есть отсутствующие эквиваленты, которые заменяются специальным символом или отображаются как их двоичный ASCII-эквивалент (fe-i поэтому хотел бы ссылаться на следующий пост-пост: Специальные символы UTF-8 не отображаются).
Итак, на мой взгляд, у вас есть два возможных способа справиться с этим:
- Преобразовать все символы из utf8 в что-то обрабатываемое - f.e. ASCII - это можно сделать f.e. с
iconv -f utf-8 -t ascii -o file_in_ascii.txt file_in_utf8.txt
. Но будьте осторожны, перейдя от одного более широкого символьного пространства (utf) к меньшему, что может привести к потере данных.
- Правильно отредактируйте utf (8) - это то, как мир пишет материал. Если вы считаете, что вам, возможно, придется полагаться на ASCII-символы из-за какого-либо ограничения постобработки, остановите и переосмыслите. В большинстве случаев постпроцессор уже поддерживает utf, вероятно, лучше узнать, как его использовать. Вы делаете свои вещи будущими и пуленепробиваемыми.
Обработка utf может показаться сложной, следующие шаги могут помочь вам выполнить utf-готовность:
- Уметь правильно отображать utf или гарантировать, что ваш стек отображения (os, terminal и т.д.) способен отображать соответствующий поднабор unicode (который, конечно же, должен соответствовать вашим потребностям), это может помешать необходимости из hex-редактора во многих случаях. К сожалению, utf слишком велик, чтобы получить один шрифт, но хороший момент для начала - это так-сообщение: https://stackoverflow.com/questions/586503/complete-monospaced-unicode-font
- Уметь фильтровать недействительные байтовые последовательности. И есть много способов добиться этого, этот ul-post показывает множество разнообразных способов: Фильтрация недействительного utf8 - я хочу особо указать 4-й ответ, который предлагает использовать
uconv
, который позволяет вам установить обработчик обратного вызова для недопустимых последовательностей.
- Читайте немного больше о юникоде.
Ответ 7
Очень грязное решение в 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}
Ответ 8
Следующая программа 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;
}