Спецификация типов данных ASCII, ISO-8859, UTF-8 Unicode в программе C

Я пытаюсь создать программу c, которая принимает файл в качестве входных данных из командной строки и определяет тип файлов. Мои варианты

  1. пустой
  2. ASCII-текст
  3. Текст ISO-8859
  4. Юникод UTF-8

Когда мне нужно создать операторы if, которые я написал для ASCII:

if(c != EOF && c <= 127)

Для ISO-8859 я написал:

if((c != EOF && c <= 127) || (c >= 160 && c<= 255))

Эти два работают, когда я передаю им файлы с входными данными, которые они должны указать. Однако, когда я добрался до Unicode UTF-8, мой оператор if выглядел так:

if(c != EOF && c <= 255)

И это не работает. Я продолжаю получать неправильный результат.

Может ли кто-нибудь помочь мне в том, как указать текст Unicode UTF-8 дальше?

Спасибо


person Gog    schedule 08.08.2017    source источник
comment
Возможно, вам поможет ЭТО.   -  person kocica    schedule 08.08.2017


Ответы (1)


UTF-8 не поддерживает диапазоны 192-193 и 245-255; однако они не так часто оказываются такими в тексте ISO-8859-1, и лично я бы не стал полагаться на «разрыв 120–160», поскольку Windows-1252 часто используется взаимозаменяемо как ISO-8859-11, его нет.

Гораздо более надежный способ определить, является ли файл UTF-8, — вместо простой проверки диапазонов байтов проверить, соответствуют ли его многобайтовые последовательности к "синтаксису" UTF-8.

FILE *fp = ...;
int ch;
bool good_utf8 = true;
bool good_ascii = true;
bool empty = true;
bool good_iso8859_1 = true;
while((ch=fgetc(fp))!=EOF) {
    empty = false;
    int extra = 0;
    if(ch>>7 == 0) {
        // ok, if the high bit is not set it's a "regular" character
    } else {
        // ASCII never has the high bit set
        good_ascii = false;
        // ISO8859-1 gap
        if(ch>=120 && ch<= 160) good_iso8859_1 = false;
        // check if it's a valid UTF-8 multibyte sequence
        if((ch>>5) == 6) {
            // 110xxxxx => one continuation byte
            extra = 1;
        } else if((ch>>4) == 14) {
            // 1110xxxx => two continuation bytes
            extra = 2;
        } else if((ch>>3) == 30) {
            // 11110xxx => three continuation bytes
            extra = 3;
        } else {
            // there's no other valid UTF-8 sequence prefix
            good_utf8 = false;
        }
    }
    for(; good_utf8 && extra > 0; --extra) {
        ch = fgetc(fp);
        if(ch>=120 && ch<= 160) good_iso8859_1 = false;
        // all the stated continuation bytes must be present,
        // and they have to follow the 10xxxxxx pattern
        if(ch==EOF || ((ch>>6) != 2)) {
            good_utf8 = false;
        }
    }
}
fclose(fp);

  1. ISO-8859 - это не одна кодировка, а несколько связанных; Я предполагаю, что вы говорите об ISO-8859-1 (также известном как «Latin1»), потому что вы говорите о разрыве 120-160; если вместо этого вам нужно определить какой вариант ISO-8859, вы должны проверить наличие различных пробелов.
person Matteo Italia    schedule 08.08.2017
comment
Намного более надежный способ определить, является ли файл UTF-8, это... проверить, соответствуют ли его многобайтовые последовательности синтаксису UTF-8 - то же самое касается других форматов. ASCII является подмножеством UTF-8, поэтому файл ASCII пройдет тест UTF-8, но вы должны проверить, находятся ли байты в таблице определенных символов ASCII. Это легко сделать с помощью простого теста <= 127... - person Remy Lebeau; 09.08.2017
comment
... Но вы не можете просто выполнить простую проверку диапазона, например <= 255 (что, кстати, всегда будет истинным, поскольку байт никогда не может превышать 255) с различными форматами ISO-8859-X, потому что каждый ISO-8859 определяет разные диапазоны байтов как действительные (и в некоторых из них есть пробелы), и они определяют общие байты за пределами диапазона ASCII как сопоставление с разными кодовыми точками Unicode. Чтобы обнаружить определенный формат ISO-8859-X, вы должны проверить отдельные байты файла на соответствие таблице определенных значений этого формата. Нет единого теста, чтобы сказать, что файл соответствует стандарту ISO-8859, вы должны тестировать каждый из них, пока не найдете совпадение. - person Remy Lebeau; 09.08.2017
comment
Да, код в ответе, по сути, проверяет, iso8859-1 ли я, как как-то подразумевал OP; тем не менее, общая идея состоит в том, чтобы идти путем исключения - ASCII легко, UTF-8 легко, если это не любой из двух, вероятно, это какой-то ISO-8859. - person Matteo Italia; 09.08.2017
comment
@RemyLebeau: также: то же самое относится и к другим форматам - не совсем, UTF-8 отличается, поскольку кодировки ISO-8859 не имеют многобайтовых последовательностей с фиксированным, хорошо узнаваемым синтаксисом - они могут иметь пробелы в таблице символов, но как только критические символы избегаются, любая последовательность исчезает, поэтому их гораздо труднее надежно обнаружить (по этой причине большинство хороших угадывателей кодировки после исключения невозможных кодировок с запрещенными диапазонами сравнивают частоты символов со средними таблицами частоты символов для используемых языков в заданной кодировке). - person Matteo Italia; 09.08.2017