如何逐行读取unicode(utf-8)/二进制文件

时间:2010-01-21 22:01:05

标签: c windows encoding utf-8

程序员,

我想逐行读取由记事本创建的Unicode(UTF-8)文本文件,我不想在屏幕上显示Unicode字符串,我只想阅读并比较字符串!。

此代码逐行读取ANSI文件,并比较字符串

我想要什么

逐行阅读test_ansi.txt

如果line =“b”打印“YES!”

否则打印“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)文件,经过6个多月我没有在C中找到任何好的代码/库可以读取以UTF-8编码的文件!,我不知道确切原因,但我认为标准C不支持Unicode!

读取Unicode二进制文件OK!,但问题是二进制文件中已经创建的二进制文件!这意味着如果我们想要读取由记事本创建的Unicode(UTF-8)文件,我们需要从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 如果line =“ب”打印“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 个答案:

答案 0 :(得分:6)

UTF-8的一个不错的属性是需要解码才能进行比较。无论您是先解码,strcmp返回的顺序都是相同的。所以只需将其作为原始字节读取并运行strcmp。

答案 1 :(得分:5)

我找到了解决问题的方法,我想与任何有兴趣在C99中阅读UTF-8文件的人分享解决方案。

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 :(得分:2)

如果您使用Visual Studio 2005及更高版本,

fgets()可以解码UTF-8编码的文件。像这样更改你的代码:

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

答案 3 :(得分:2)

在本文中,编写了一个编码和解码例程 它解释了如何编码unicode:

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

可以轻松调整为C. 只需编码您的ANSI或解码UTF-8字符串并创建一个字节 比较

编辑:在OP之后说从C ++重写函数太难了 这是一个模板:

需要什么:
+释放分配的内存(或等到过程结束或忽略它)
+添加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);
}

答案 4 :(得分:2)

我知道我很糟糕......但你甚至不考虑BOM!这里的大多数例子都会失败。

编辑:

字节顺序标记在文件的beginnig处是几个字节,可用于标识文件的编码。有些编辑添加它们,很多时候它们只是以各种方式破解(我记得因为这个问题而在几分钟内解决PHP头问题)。

一些RTFM: http://en.wikipedia.org/wiki/Byte_order_mark http://blogs.msdn.com/oldnewthing/archive/2004/03/24/95235.aspx What is XML BOM and how do I detect it?

答案 5 :(得分:0)

只是为了解决BOM参数。这是来自记事本的文件

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

在开始时使用BOM

我个人认为不应该有BOM(因为它是一个字节格式),但那不是重点