Как обнаружить UTF-8 в простой C? - PullRequest
33 голосов
/ 23 июня 2009

Я ищу фрагмент кода в простом старом C, который обнаруживает, что данная строка в кодировке UTF-8. Я знаю решение с помощью регулярных выражений, но по разным причинам было бы лучше избегать использования чего-либо, кроме простого C в этом конкретном случае.

Решение с регулярным выражением выглядит следующим образом (предупреждение: различные проверки опущены):

#define UTF8_DETECT_REGEXP  "^([\x09\x0A\x0D\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})*$"

const char *error;
int         error_off;
int         rc;
int         vect[100];

utf8_re = pcre_compile(UTF8_DETECT_REGEXP, PCRE_CASELESS, &error, &error_off, NULL);
utf8_pe = pcre_study(utf8_re, 0, &error);

rc = pcre_exec(utf8_re, utf8_pe, str, len, 0, 0, vect, sizeof(vect)/sizeof(vect[0]));

if (rc > 0) {
    printf("string is in UTF8\n");
} else {
    printf("string is not in UTF8\n")
}

Ответы [ 10 ]

44 голосов
/ 23 июня 2009

Вот (надеюсь, без ошибок) реализация этого выражения в простом C:

_Bool is_utf8(const char * string)
{
    if(!string)
        return 0;

    const unsigned char * bytes = (const unsigned char *)string;
    while(*bytes)
    {
        if( (// ASCII
             // use bytes[0] <= 0x7F to allow ASCII control characters
                bytes[0] == 0x09 ||
                bytes[0] == 0x0A ||
                bytes[0] == 0x0D ||
                (0x20 <= bytes[0] && bytes[0] <= 0x7E)
            )
        ) {
            bytes += 1;
            continue;
        }

        if( (// non-overlong 2-byte
                (0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
                (0x80 <= bytes[1] && bytes[1] <= 0xBF)
            )
        ) {
            bytes += 2;
            continue;
        }

        if( (// excluding overlongs
                bytes[0] == 0xE0 &&
                (0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
                (0x80 <= bytes[2] && bytes[2] <= 0xBF)
            ) ||
            (// straight 3-byte
                ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
                    bytes[0] == 0xEE ||
                    bytes[0] == 0xEF) &&
                (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
                (0x80 <= bytes[2] && bytes[2] <= 0xBF)
            ) ||
            (// excluding surrogates
                bytes[0] == 0xED &&
                (0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
                (0x80 <= bytes[2] && bytes[2] <= 0xBF)
            )
        ) {
            bytes += 3;
            continue;
        }

        if( (// planes 1-3
                bytes[0] == 0xF0 &&
                (0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
                (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
                (0x80 <= bytes[3] && bytes[3] <= 0xBF)
            ) ||
            (// planes 4-15
                (0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
                (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
                (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
                (0x80 <= bytes[3] && bytes[3] <= 0xBF)
            ) ||
            (// plane 16
                bytes[0] == 0xF4 &&
                (0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
                (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
                (0x80 <= bytes[3] && bytes[3] <= 0xBF)
            )
        ) {
            bytes += 4;
            continue;
        }

        return 0;
    }

    return 1;
}

Обратите внимание, что это точный перевод регулярного выражения, рекомендованного W3C для проверки формы, который действительно отклоняет некоторые допустимые последовательности UTF-8 (в частности, те, которые содержат управляющие символы ASCII).

Кроме того, даже после исправления путем внесения изменений, упомянутых в комментарии, он по-прежнему предполагает нулевое завершение, что предотвращает встраивание символов NUL, хотя технически это должно быть допустимо.

Когда я занимался созданием своей собственной библиотеки строк, я пошел с измененным UTF-8 (то есть кодировал NUL как длинную двухбайтовую последовательность) - не стесняйтесь использовать этот заголовок в качестве шаблона для предоставления процедура проверки, которая не страдает от вышеуказанных недостатков.

30 голосов
/ 03 марта 2014

Этот декодер от Bjoern Hoermann - самый простой, который я нашел. Он также работает, передавая ему один байт, а также сохраняя состояние. Состояние очень полезно для анализа UTF8, поступающего частями по сети.

http://bjoern.hoehrmann.de/utf-8/decoder/dfa/

// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.

#define UTF8_ACCEPT 0
#define UTF8_REJECT 1

static const uint8_t utf8d[] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
  8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
  0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
  0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
  0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
  1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
  1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
  1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
};

uint32_t inline
decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
  uint32_t type = utf8d[byte];

  *codep = (*state != UTF8_ACCEPT) ?
    (byte & 0x3fu) | (*codep << 6) :
    (0xff >> type) & (byte);

  *state = utf8d[256 + *state*16 + type];
  return *state;
}

Простой валидатор / детектор не нуждается в кодовой точке, поэтому его можно записать так (начальное состояние установлено на UTF8_ACCEPT):

uint32_t validate_utf8(uint32_t *state, char *str, size_t len) {
   size_t i;
   uint32_t type;

    for (i = 0; i < len; i++) {
        // We don't care about the codepoint, so this is
        // a simplified version of the decode function.
        type = utf8d[(uint8_t)str[i]];
        *state = utf8d[256 + (*state) * 16 + type];

        if (*state == UTF8_REJECT)
            break;
    }

    return *state;
}

Если текст верный utf8 UTF8_ACCEPT возвращается. Если это неверно UTF8_REJECT. Если требуется больше данных, возвращается другое целое число.

Пример использования с подачей данных кусками (например, из сети):

char buf[128];
size_t bytes_read;
uint32_t state = UTF8_ACCEPT;

// Validate the UTF8 data in chunks.
while ((bytes_read = get_new_data(buf, sizeof(buf))) {
    if (validate_utf8(&state, buf, bytes_read) == UTF8_REJECT)) {
        fprintf(stderr, "Invalid UTF8 data!\n");
        return -1;
    }
}

// If everything went well we should have proper UTF8,
// the data might instead have ended in the middle of a UTF8
// codepoint.
if (state != UTF8_ACCEPT) {
    fprintf(stderr, "Invalid UTF8, incomplete codepoint\n");
}
9 голосов
/ 23 июня 2009

Вы не можете определить, является ли данная строка (или последовательность байтов) текстом в кодировке UTF-8, как, например, каждая серия октетов UTF-8 также является действительной (если не имеет смысла) последовательностью Latin-1 (или некоторыми другое кодирование) октеты. Однако не каждая серия действительных октетов Latin-1 является действительной серией UTF-8. Таким образом, вы можете исключить строки, которые не соответствуют схеме кодировки UTF-8:

U+0000-U+007F    0xxxxxxx
U+0080-U+07FF    110yyyxx    10xxxxxx
U+0800-U+FFFF    1110yyyy    10yyyyxx    10xxxxxx
U+10000-U+10FFFF 11110zzz    10zzyyyy    10yyyyxx    10xxxxxx   
6 голосов
/ 23 июня 2009

Вам нужно проанализировать строку как UTF-8, см. http://www.rfc -editor.org / rfc / rfc3629.txt Это очень просто. Если синтаксический анализ не выполняется, это не UTF-8. Есть несколько простых библиотек UTF-8, которые могут это сделать.

Возможно, это можно упростить, если вы знаете , что строка является либо простым старым ASCII , либо , она содержит символы вне ASCII, которые закодированы в UTF-8. В этом случае вам часто не нужно заботиться о разнице, дизайн UTF-8 заключался в том, что существующие программы, которые могли обрабатывать ASCII, в большинстве случаев могли прозрачно обрабатывать UTF-8.

Имейте в виду, что ASCII кодируется в UTF-8 как таковой, поэтому ASCII действителен в UTF-8.

Строка переменного тока может быть чем угодно, это проблема, которую вам нужно решить, и вы не знаете, является ли содержимое ASCII, GB 2312, CP437, UTF-16 или любым другим десятком кодировок символов, которые делают жизнь программ тяжело ..?

2 голосов
/ 04 марта 2014

Невозможно обнаружить, что данный массив байтов является строкой UTF-8. Вы можете достоверно определить, что он не может быть действительным UTF-8 (что не означает, что он не является недействительным UTF-8); и вы можете определить, что это может быть действительная последовательность UTF-8, но это может привести к ложным срабатываниям.

Для простого примера используйте генератор случайных чисел, чтобы сгенерировать массив из 3 случайных байтов, и используйте его для проверки вашего кода. Это случайные байты и, следовательно, не UTF-8, поэтому каждая строка, которую ваш код считает «возможно, UTF-8», является ложноположительным. Я предполагаю, что (при этих условиях) ваш код будет неправильным в течение 12% времени.

Как только вы поймете, что это невозможно, вы можете начать думать о возвращении уровня доверия (в дополнение к вашему прогнозу). Например, ваша функция может возвращать что-то вроде «Я на 88% уверен, что это UTF-8».

Теперь сделайте это для всех других типов данных. Например, у вас может быть функция, которая проверяет, являются ли данные UTF-16, которая может возвращать «Я на 95% уверен, что это UTF-16», а затем решить, что (поскольку 95% выше, чем 88%), это более вероятно, что данные были UTF-16, а не UTF-8.

Следующим шагом является добавление трюков для повышения уровня доверия. Например, если строка, по-видимому, в основном содержит группы допустимых слогов, разделенных пробелами, то вы можете быть намного увереннее, что это на самом деле UTF-8. Точно так же, если данные могут быть HTML, вы можете проверить наличие допустимой HTML-разметки и использовать ее для повышения вашей уверенности.

Конечно, то же самое относится и к другим типам данных. Например, если данные имеют действительный заголовок PE32 или ELF или правильный заголовок BMP, JPG или MP3, вы можете быть намного увереннее, что это вообще не UTF-8.

Гораздо лучший подход - установить реальную причину проблемы. Например, может быть возможно добавить какой-то идентификатор «типа документа» в начало всех файлов, которые вас интересуют, или, возможно, сказать «это программное обеспечение предполагает UTF-8 и больше ничего не поддерживает»; так что вам не нужно делать хитрые догадки.

2 голосов
/ 23 июня 2009

Вы можете использовать детектор UTF-8, встроенный в Firefox . Он обнаружен в универсальном детекторе кодировки и в значительной степени похож на библиотеку C ++. Должно быть очень легко найти класс, распознающий UTF-8, и взять только это.
В основном этот класс обнаруживает последовательности символов, уникальные для UTF-8.

  • получить последний ствол Firefox
  • Перейдите в \ mozilla \ extensions \ universalchardet \
  • найти класс детектора UTF-8 (я не совсем помню, как его точное название)
1 голос
/ 02 июня 2014

3 случайных байта, по моим подсчетам, с вероятностью 15.8% могут быть действительными UTF-8:

128 ^ 3 возможных последовательностей только для ASCII = 2097152

2 ^ 16-2 ^ 11 возможных 3-байтовых символов UTF-8 (предполагается, что допускаются суррогатные пары и нехарактерные символы) = 63488

1920 2-байтовые символы UTF-8 до или после символа ASCII = 1920 * 128 * 2 = 524288

Разделить на количество 3-байтовых последовательностей = (2097152 + 63488 + 491520) /16777216.0 = 0,1580810546875

ИМХО, это сильно переоценивает количество неверных совпадений, потому что файл имеет длину всего 3 байта. Пересечение идет вниз по мере увеличения количества байтов. Также фактический текст в не-UTF-8 не является случайным, есть большое количество одиночных байтов с установленным старшим битом, что недопустимо в UTF-8.

Более полезный показатель для угадывания вероятности сбоя - это вероятность того, что последовательность байтов с установленным старшим битом является действительной UTF-8. Я получаю эти значения:

1 byte = 0% # the really important number that is often ignored
2 byte = 11.7%
3 byte = 3.03% (assumes surrogate halves are valid)
4 byte = 1.76% (includes two 2-byte characters)

Также полезно попытаться найти фактическую читаемую строку (на любом языке и в любой кодировке), которая также является допустимой строкой UTF-8. Это очень и очень сложно, указывая на то, что это не проблема с реальными данными.

0 голосов
/ 31 июля 2018

Я знаю, что это старая ветка, но я решил опубликовать свое решение здесь, так как считаю, что это улучшение по сравнению с замечательным решением @Christoph (за которое я проголосовал).

Я не эксперт, поэтому, возможно, я неправильно прочитал RFC, но мне кажется, что вместо 256-байтовой карты можно использовать 32-байтовую карту, что экономит память и время.

Это привело меня к простому макросу, который продвигает указатель строки на один символ UTF-8, сохраняя кодовую точку UTF8 в 32-битном целом числе со знаком и сохраняя значение -1 в случае ошибки.

Вот код с некоторыми комментариями.

#include <stdint.h>
/**
 * Maps the last 5 bits in a byte (0b11111xxx) to a UTF-8 codepoint length.
 *
 * Codepoint length 0 == error.
 *
 * The first valid length can be any value between 1 to 4 (5== error).
 *
 * An intermidiate (second, third or forth) valid length must be 5.
 *
 * To map was populated using the following Ruby script:
 *
 *      map = []; 32.times { map << 0 }; (0..0b1111).each {|i| map[i] = 1} ;
 *      (0b10000..0b10111).each {|i| map[i] = 5} ;
 *      (0b11000..0b11011).each {|i| map[i] = 2} ;
 *      (0b11100..0b11101).each {|i| map[i] = 3} ;
 *      map[0b11110] = 4; map;
 */
static uint8_t fio_str_utf8_map[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                                     1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5,
                                     5, 5, 2, 2, 2, 2, 3, 3, 4, 0};

/**
 * Advances the `ptr` by one utf-8 character, placing the value of the UTF-8
 * character into the i32 variable (which must be a signed integer with 32bits
 * or more). On error, `i32` will be equal to `-1` and `ptr` will not step
 * forwards.
 *
 * The `end` value is only used for overflow protection.
 */
#define FIO_STR_UTF8_CODE_POINT(ptr, end, i32)                                 \
  switch (fio_str_utf8_map[((uint8_t *)(ptr))[0] >> 3]) {                      \
  case 1:                                                                      \
    (i32) = ((uint8_t *)(ptr))[0];                                             \
    ++(ptr);                                                                   \
    break;                                                                     \
  case 2:                                                                      \
    if (((ptr) + 2 > (end)) ||                                                 \
        fio_str_utf8_map[((uint8_t *)(ptr))[1] >> 3] != 5) {                   \
      (i32) = -1;                                                              \
      break;                                                                   \
    }                                                                          \
    (i32) =                                                                    \
        ((((uint8_t *)(ptr))[0] & 31) << 6) | (((uint8_t *)(ptr))[1] & 63);    \
    (ptr) += 2;                                                                \
    break;                                                                     \
  case 3:                                                                      \
    if (((ptr) + 3 > (end)) ||                                                 \
        fio_str_utf8_map[((uint8_t *)(ptr))[1] >> 3] != 5 ||                   \
        fio_str_utf8_map[((uint8_t *)(ptr))[2] >> 3] != 5) {                   \
      (i32) = -1;                                                              \
      break;                                                                   \
    }                                                                          \
    (i32) = ((((uint8_t *)(ptr))[0] & 15) << 12) |                             \
            ((((uint8_t *)(ptr))[1] & 63) << 6) |                              \
            (((uint8_t *)(ptr))[2] & 63);                                      \
    (ptr) += 3;                                                                \
    break;                                                                     \
  case 4:                                                                      \
    if (((ptr) + 4 > (end)) ||                                                 \
        fio_str_utf8_map[((uint8_t *)(ptr))[1] >> 3] != 5 ||                   \
        fio_str_utf8_map[((uint8_t *)(ptr))[2] >> 3] != 5 ||                   \
        fio_str_utf8_map[((uint8_t *)(ptr))[3] >> 3] != 5) {                   \
      (i32) = -1;                                                              \
      break;                                                                   \
    }                                                                          \
    (i32) = ((((uint8_t *)(ptr))[0] & 7) << 18) |                              \
            ((((uint8_t *)(ptr))[1] & 63) << 12) |                             \
            ((((uint8_t *)(ptr))[2] & 63) << 6) |                              \
            (((uint8_t *)(ptr))[3] & 63);                                      \
    (ptr) += 4;                                                                \
    break;                                                                     \
  default:                                                                     \
    (i32) = -1;                                                                \
    break;                                                                     \
  }

/** Returns 1 if the String is UTF-8 valid and 0 if not. */
inline static size_t fio_str_utf8_valid2(char const *str, size_t length) {
  if (!str)
    return 0;
  if (!length)
    return 1;
  const char *const end = str + length;
  int32_t c = 0;
  do {
    FIO_STR_UTF8_CODE_POINT(str, end, c);
  } while (c > 0 && str < end);
  return str == end && c >= 0;
}
0 голосов
/ 11 апреля 2017

В основном я проверяю, соответствует ли данный ключ (строка максимум 4 символа) формату по этой ссылке: http://www.fileformat.info/info/unicode/utf8.htm

/*
** Checks if the given string has all bytes like: 10xxxxxx
** where x is either 0 or 1
*/

static int      chars_are_folow_uni(const unsigned char *chars)
{
    while (*chars)
    {
        if ((*chars >> 6) != 0x2)
            return (0);
        chars++;
    }
    return (1);
}

int             char_is_utf8(const unsigned char *key)
{
    int         required_len;

    if (key[0] >> 7 == 0)
        required_len = 1;
    else if (key[0] >> 5 == 0x6)
        required_len = 2;
    else if (key[0] >> 4 == 0xE)
        required_len = 3;
    else if (key[0] >> 5 == 0x1E)
        required_len = 4;
    else
        return (0);
    return (strlen(key) == required_len && chars_are_folow_uni(key + 1));
}

У меня отлично работает:

unsigned char   buf[5];

ft_to_utf8(L'歓', buf);
printf("%d\n", char_is_utf8(buf)); // => 1
0 голосов
/ 10 мая 2015

Следующая программа читает строки utf-8 (ascii, non ascii chars, такие как euro и т.д ...) из стандартного ввода. Каждая строка передается func_find_utf8. Так как символы utf-8 являются многобайтовыми символами, функция func_find_utf8 проверяет биты символов, чтобы найти любой другой символ или не ascii. Если символ не является ascii, знайте ширину байтов. Передайте ширину байтов и поместите ее в функцию print_non_ascii.

#include<stdio.h>

#include<string.h>

/* UTF-8 : BYTE_BITS*/

/* B0_BYTE : 0XXXXXXX */

/* B1_BYTE : 10XXXXXX */

/* B2_BYTE : 110XXXXX */

/* B3_BYTE : 1110XXXX */

/* B4_BYTE : 11110XXX */

/* B5_BYTE : 111110XX */

/* B6_BYTE : 1111110X */

#define B0_BYTE 0x00

#define B1_BYTE 0x80

#define B2_BYTE 0xC0

#define B3_BYTE 0xE0

#define B4_BYTE 0xF0

#define B5_BYTE 0xF8

#define B6_BYTE 0xFC

#define B7_BYTE 0xFE

/* Please tune this as per number of lines input */

#define MAX_UTF8_STR 10

/* 600 is used because 6byteX100chars */

#define MAX_UTF8_CHR 600

void func_find_utf8 (char *ptr_to_str);

void print_non_ascii (int bytes, char *pbyte);

char strbuf[MAX_UTF8_STR][MAX_UTF8_CHR];

int
main (int ac, char *av[])
{

  int i = 0;

  char no_newln_str[MAX_UTF8_CHR];

  i = 0;

  printf ("\n\nYou can enter utf-8 string or Q/q to QUIT\n\n");

  while (i < MAX_UTF8_STR)
    {

      fgets (strbuf[i], MAX_UTF8_CHR, stdin);

      if (!strlen (strbuf[i]))
    break;

      if ((strbuf[i][0] == 'Q') || (strbuf[i][0] == 'q'))
    break;

      strcpy (no_newln_str, strbuf[i]);

      no_newln_str[strlen (no_newln_str) - 1] = 0;

      func_find_utf8 (no_newln_str);

      ++i;

    }

  return 1;

}

void
func_find_utf8 (char *ptr_to_str)
{

  int found_non_ascii;

  char *pbyte;

  pbyte = ptr_to_str;

  found_non_ascii = 0;

  while (*pbyte)
    {

      if ((*pbyte & B1_BYTE) == B0_BYTE)
    {

      pbyte++;

      continue;

    }

      else
    {

      found_non_ascii = 1;

      if ((*pbyte & B7_BYTE) == B6_BYTE)
        {

          print_non_ascii (6, pbyte);

          pbyte += 6;

          continue;

        }

      if ((*pbyte & B6_BYTE) == B5_BYTE)
        {

          print_non_ascii (5, pbyte);

          pbyte += 5;

          continue;

        }

      if ((*pbyte & B5_BYTE) == B4_BYTE)
        {

          print_non_ascii (4, pbyte);

          pbyte += 4;

          continue;

        }

      if ((*pbyte & B4_BYTE) == B3_BYTE)
        {

          print_non_ascii (3, pbyte);

          pbyte += 3;

          continue;

        }

      if ((*pbyte & B3_BYTE) == B2_BYTE)
        {

          print_non_ascii (2, pbyte);

          pbyte += 2;

          continue;

        }

    }

    }

  if (found_non_ascii)
    printf (" These are Non Ascci chars\n");

}

void
print_non_ascii (int bytes, char *pbyte)
{

  char store[6];

  int i;

  memset (store, 0, 6);

  memcpy (store, pbyte, bytes);

  i = 0;

  while (i < bytes)
    printf ("%c", store[i++]);

  printf ("%c", ' ');

  fflush (stdout);

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...