Как читать юникод (utf-8) / двоичный файл построчно - PullRequest
12 голосов
/ 22 января 2010

Привет, программисты,

Я хочу читать построчно текстовый файл Unicode (UTF-8), созданный Notepad, я не хочу отображать строку Unicode на экране, я хочу просто читать и сравнивать строки!.

Этот код читает строку файла ANSI построчно и сравнивает строки

Что я хочу

Читать test_ansi.txt построчно

если строка = "b", выведите "YES!"

иначе выведите «НЕТ!»

read_ansi_line_by_line.c

#include <stdio.h>

int main()
{
    char *inname = "test_ansi.txt";
    FILE *infile;
    char line_buffer[BUFSIZ]; /* BUFSIZ is defined if you include stdio.h */
    char line_number;

    infile = fopen(inname, "r");
    if (!infile) {
        printf("\nfile '%s' not found\n", inname);
        return 0;
    }
    printf("\n%s\n\n", inname);

    line_number = 0;
    while (fgets(line_buffer, sizeof(line_buffer), infile)) {
        ++line_number;
        /* note that the newline is in the buffer */
        if (strcmp("b\n", line_buffer) == 0 ){
            printf("%d: YES!\n", line_number);
        }else{
            printf("%d: NO!\n", line_number,line_buffer);
        }
    }
    printf("\n\nTotal: %d\n", line_number);
    return 0;
}

test_ansi.txt

a
b
c

Компиляция

gcc -o read_ansi_line_by_line read_ansi_line_by_line.c

выход

test_ansi.txt

1: NO!
2: YES!
3: NO!


Total: 3

Теперь мне нужно прочитать файл Unicode (UTF-8), созданный Notepad, после более чем 6 месяцев я не нашел ни одного хорошего кода / библиотеки в C, который мог бы читать файл, кодированный в UTF-8 !, я не знаю именно поэтому, но я думаю, что стандартный C не поддерживает Unicode!

Чтение двоичного файла Unicode - нормально, но проблема в том, что двоичный файл чаще всего уже создается в двоичном режиме !, это означает, что если мы хотим прочитать файл Unicode (UTF-8), созданный Notepad, нам нужно перевести его из Файл UTF-8 в двоичный файл!

Этот код записывает строку Unicode в двоичный файл, ПРИМЕЧАНИЕ. Файл C кодируется в UTF-8 и компилируется GCC

.

Что я хочу

Записать символ Unicode "ب" в test_bin.dat

create_bin.c

#define UNICODE
#ifdef UNICODE
#define _UNICODE
#else
#define _MBCS
#endif

#include <stdio.h>
#include <wchar.h>

int main()
{
     /*Data to be stored in file*/
     wchar_t line_buffer[BUFSIZ]=L"ب";
     /*Opening file for writing in binary mode*/
     FILE *infile=fopen("test_bin.dat","wb");
     /*Writing data to file*/
     fwrite(line_buffer, 1, 13, infile);
     /*Closing File*/
     fclose(infile);

    return 0;
}

1042 * Компиляция * gcc -o create_bin create_bin.c выход

create test_bin.dat

Теперь я хочу прочитать двоичный файл построчно и сравнить!

Что я хочу

Читать test_bin.dat построчно если строка = "ب" выведите "YES!" иначе выведите «НЕТ!»

read_bin_line_by_line.c

#define UNICODE
#ifdef UNICODE
#define _UNICODE
#else
#define _MBCS
#endif

#include <stdio.h>
#include <wchar.h>

int main()
{
    wchar_t *inname = L"test_bin.dat";
    FILE *infile;
    wchar_t line_buffer[BUFSIZ]; /* BUFSIZ is defined if you include stdio.h */

    infile = _wfopen(inname,L"rb");
    if (!infile) {
        wprintf(L"\nfile '%s' not found\n", inname);
        return 0;
    }
    wprintf(L"\n%s\n\n", inname);

    /*Reading data from file into temporary buffer*/
    while (fread(line_buffer,1,13,infile)) {
        /* note that the newline is in the buffer */
        if ( wcscmp ( L"ب" , line_buffer ) == 0 ){
             wprintf(L"YES!\n");
        }else{
             wprintf(L"NO!\n", line_buffer);
        }
    }
    /*Closing File*/
    fclose(infile);
    return 0;
}

выход

test_bin.dat

YES!

ПРОБЛЕМА

Этот метод ОЧЕНЬ ДЛИТЕЛЬНЫЙ! и НЕ МОЩНЫЙ (я новичок в разработке программного обеспечения)

Пожалуйста, кто-нибудь знает, как читать Unicode-файл? (Я знаю, это не легко!) Кто-нибудь знает, как конвертировать файл Unicode в двоичный файл? (простой метод) Кто-нибудь знает, пожалуйста, как прочитать файл Unicode в двоичном режиме? (я не уверен)

Спасибо.

Ответы [ 6 ]

6 голосов
/ 23 января 2010

Хорошим свойством UTF-8 является то, что вам не нужно декодировать, чтобы сравнить его. Порядок, возвращаемый из strcmp, будет одинаковым независимо от того, декодируете ли вы его сначала или нет. Так что просто прочитайте его как необработанные байты и запустите strcmp.

5 голосов
/ 25 января 2010

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

void ReadUTF8(FILE* fp)
{
    unsigned char iobuf[255] = {0};
    while( fgets((char*)iobuf, sizeof(iobuf), fp) )
    {
            size_t len = strlen((char *)iobuf);
            if(len > 1 &&  iobuf[len-1] == '\n')
                iobuf[len-1] = 0;
            len = strlen((char *)iobuf);
            printf("(%d) \"%s\"  ", len, iobuf);
            if( iobuf[0] == '\n' )
                printf("Yes\n");
            else
                printf("No\n");
    }
}

void ReadUTF16BE(FILE* fp)
{
}

void ReadUTF16LE(FILE* fp)
{
}

int main()
{
    FILE* fp = fopen("test_utf8.txt", "r");
    if( fp != NULL)
    {
        // see http://en.wikipedia.org/wiki/Byte-order_mark for explaination of the BOM
        // encoding
        unsigned char b[3] = {0};
        fread(b,1,2, fp);
        if( b[0] == 0xEF && b[1] == 0xBB)
        {
            fread(b,1,1,fp); // 0xBF
            ReadUTF8(fp);
        }
        else if( b[0] == 0xFE && b[1] == 0xFF)
        {
            ReadUTF16BE(fp);
        }
        else if( b[0] == 0 && b[1] == 0)
        {
            fread(b,1,2,fp); 
            if( b[0] == 0xFE && b[1] == 0xFF)
                ReadUTF16LE(fp);
        }
        else
        {
            // we don't know what kind of file it is, so assume its standard
            // ascii with no BOM encoding
            rewind(fp);
            ReadUTF8(fp);
        }
    }        

    fclose(fp);
}
2 голосов
/ 22 января 2010

Я знаю, что я плохой ... но вы даже не принимаете во внимание спецификацию! Большинство примеров здесь потерпит неудачу.

EDIT:

Метки порядка байтов - это несколько байтов в начале файла, которые можно использовать для определения кодировки файла. Некоторые редакторы добавляют их, и много раз они просто портят вещи (я помню, как несколько минут боролся с проблемами с заголовками PHP).

Некоторые RTFM: http://en.wikipedia.org/wiki/Byte_order_mark http://blogs.msdn.com/oldnewthing/archive/2004/03/24/95235.aspx Что такое спецификация XML и как ее обнаружить?

2 голосов
/ 22 января 2010

В этой статье написана процедура кодирования и декодирования объясняется, как кодируется Unicode:

http://www.codeguru.com/cpp/misc/misc/multi-lingualsupport/article.php/c10451/

Он может быть легко настроен на C. Просто закодируйте свой ANSI или расшифруйте строку UTF-8 и сделайте байт сравнить

РЕДАКТИРОВАТЬ: после того, как ОП сказал, что это слишком сложно переписать функцию из C ++ вот шаблон:

Что нужно:
+ Освободить выделенную память (или дождаться окончания процесса или проигнорировать его)
+ Добавить 4-байтовые функции
+ Скажите, что short и int не гарантированно будут иметь длину 2 и 4 байта (я знаю, но С действительно глупо!) И, наконец,
+ Найти другие ошибки

#include <stdlib.h>
#include <string.h>

#define         MASKBITS                0x3F
#define         MASKBYTE                0x80
#define         MASK2BYTES              0xC0
#define         MASK3BYTES              0xE0
#define         MASK4BYTES              0xF0
#define         MASK5BYTES              0xF8
#define         MASK6BYTES              0xFC

char* UTF8Encode2BytesUnicode(unsigned short* input)
{
   int size = 0,
       cindex = 0;
   while (input[size] != 0)
     size++;
   // Reserve enough place; The amount of 
   char* result = (char*) malloc(size);
   for (int i=0; i<size; i++)
   {
      // 0xxxxxxx
      if(input[i] < 0x80)
      {
         result[cindex++] = ((char) input[i]);
      }
      // 110xxxxx 10xxxxxx
      else if(input[i] < 0x800)
      {
         result[cindex++] = ((char)(MASK2BYTES | input[i] >> 6));
         result[cindex++] = ((char)(MASKBYTE | input[i] & MASKBITS));
      }
      // 1110xxxx 10xxxxxx 10xxxxxx
      else if(input[i] < 0x10000)
      {
         result[cindex++] = ((char)(MASK3BYTES | input[i] >> 12));
         result[cindex++] = ((char)(MASKBYTE | input[i] >> 6 & MASKBITS));
         result[cindex++] = ((char)(MASKBYTE | input[i] & MASKBITS));
      }
   }
}

wchar_t* UTF8Decode2BytesUnicode(char* input)
{
  int size = strlen(input);
  wchar_t* result = (wchar_t*) malloc(size*sizeof(wchar_t));
  int rindex = 0,
      windex = 0;
  while (rindex < size)
  {
      wchar_t ch;

      // 1110xxxx 10xxxxxx 10xxxxxx
      if((input[rindex] & MASK3BYTES) == MASK3BYTES)
      {
         ch = ((input[rindex] & 0x0F) << 12) | (
               (input[rindex+1] & MASKBITS) << 6)
              | (input[rindex+2] & MASKBITS);
         rindex += 3;
      }
      // 110xxxxx 10xxxxxx
      else if((input[rindex] & MASK2BYTES) == MASK2BYTES)
      {
         ch = ((input[rindex] & 0x1F) << 6) | (input[rindex+1] & MASKBITS);
         rindex += 2;
      }
      // 0xxxxxxx
      else if(input[rindex] < MASKBYTE)
      {
         ch = input[rindex];
         rindex += 1;
      }

      result[windex] = ch;
   }
}

char* getUnicodeToUTF8(wchar_t* myString) {
  int size = sizeof(wchar_t);
  if (size == 1)
    return (char*) myString;
  else if (size == 2)
    return UTF8Encode2BytesUnicode((unsigned short*) myString);
  else
    return UTF8Encode4BytesUnicode((unsigned int*) myString);
}
2 голосов
/ 22 января 2010

fgets () может декодировать файлы в кодировке UTF-8, если вы используете Visual Studio 2005 и выше. Измените свой код следующим образом:

infile = fopen(inname, "r, ccs=UTF-8");
0 голосов
/ 23 января 2010

только для расчета аргумента спецификации. Вот файл из блокнота

 [paul@paul-es5 tests]$ od -t x1 /mnt/hgfs/cdrive/test.txt
 0000000 ef bb bf 61 0d 0a 62 0d 0a 63
 0000012

с спецификацией в начале

Лично я не думаю, что должна быть спецификация (поскольку это байтовый формат), но это не главное

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