Подтвердить что ты не робот

Как обнаружить недействительный 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 или других языках?

4b9b3361

Ответ 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;
}