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

Как читать unicode (utf-8)/двоичный файл по строкам

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

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

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

Что я хочу

Прочитайте файл test_ansi.txt по строке

если строка = "b" напечатает "YES!"

else напечатайте "NO!"

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 - это OK!, но проблема в том, что бинарный файл больше всего уже создан в двоичном режиме!, что означает, что если мы хотим прочитать файл Unicode (UTF-8), созданный Notepad, нам нужно перевести его из Файл UTF-8 в файл BINARY!

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

Что я хочу

Введите Unicode char "ب" в 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;
}

Компиляция

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 в двоичном режиме? (я не уверен)

Спасибо.

4b9b3361

Ответ 1

Я нашел решение своей проблемы, и я хотел бы поделиться решением с любым, кто интересуется чтением файла 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

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

Ответ 3

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

infile = fopen(inname, "r, ccs=UTF-8");

Ответ 4

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

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

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

EDIT: после того, как OP сказал, что слишком сложно переписать функцию из С++ здесь шаблон:

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

#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);
}

Ответ 5

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

EDIT:

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

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

Ответ 6

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

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

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

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