c++ builder 中怎么判断txt(记事本)文件是不是utf8编码?(在线等)

时间:2023-01-05 09:24:24
c++ builder 中怎么判断txt(记事本)文件是不是utf8编码?
下面是一段UTF8转ASCII的代码,我想加个判断,当文件不是UTF8时就不转换,但用memcmp函数判断不管用,请问要怎么判断啊?

void UTF8FileConvertToAnsi(char *srcFile,char *destFile)
{
char UTF8Header[3] = {0xef,0xbb,0xbf};
char *srcBuff;
char *destBuff;
int hFile;
int nSize;

if (!FileExists(srcFile)) {
return;
}
hFile = FileOpen(srcFile,0);
nSize = FileSeek(hFile,0,2);
if (nSize < 3) {
FileClose(hFile);
return;
}

srcBuff = (char*)malloc(3);
FileSeek(hFile,0,0);
FileRead(hFile, srcBuff, 3);
        FileSeek(hFile,0,0);

//if (memcmp(UTF8Header,srcBuff,3)) {
        free(srcBuff);
        destBuff = (char*)malloc(nSize);
        memset(destBuff,0x00,nSize);
        srcBuff = (char*)malloc(nSize + 1);
        memset(srcBuff,0x00,nSize + 1);

        FileRead(hFile, srcBuff, nSize);
        FileClose(hFile);

        strcpy(destBuff,Utf8ToAnsi(srcBuff).c_str());

        hFile = FileCreate(destFile);
        FileWrite(hFile,destBuff,strlen(destBuff));
        FileClose(hFile);

        free(destBuff);
        free(srcBuff);
}

22 个解决方案

#1


关注

#2


UTF8编码其实和Unicode是同类,就是在编码方式上不同。

首先UTF8编码后的大小是不一定,不像Unicode编码后的大小是一样的。Unicode的编码中一个英文字母 “a” 和 一个汉字 “好”,编码后都是占用的空间大小是一样的,都是两个字节。而UTF8编码中一个英文字母“a” 和 一个汉字 “好”,编码后占用的空间大小就不样了,前者是一个字节,后者是三个字节。

UTF8编码的原理:因为一个字母还有一些键盘上的符号加起来只用二进制七位就可以表示出来,而一个字节就是八位,所以UTF8就用一个字节来表式字母和一些键盘上的符号。然而当我们拿到被编码后的一个字节后怎么知道它的组成?它有可能是英文字母的一个字节,也有可能是汉字的三个字节中的一个字节!所以,UTF8是有标志位的。
当要表示的内容是7位的时候就用一个字节:0******* 第一个0为标志位,剩下的空间正好可以表示ASCII码0-127的内容。当要表示的内容在8到11位的时候就用两个字节:110***** 10******第一个字节的110和第二个字节的10为标志位。
当要表示的内容在12到16位的时候就用三个字节:1110***** 10****** 10******和上面一样,第一个字节的1110和第二、三个字节的10都是标志位,剩下的空间正好可以表示汉字。
依次类推:
四个字节:11110**** 10****** 10****** 10****** 
五个字节:111110*** 10****** 10****** 10****** 10****** 
六个字节:1111110** 10****** 10****** 10****** 10****** 10******

#3


/* IsTextUTF8
 *
 * UTF-8 is the encoding of Unicode based on Internet Society RFC2279
 * ( See http://www.cis.ohio-state.edu/htbin/rfc/rfc2279.html )
 *
 * Basicly:
 * 0000 0000-0000 007F - 0xxxxxxx  (ascii converts to 1 octet!)
 * 0000 0080-0000 07FF - 110xxxxx 10xxxxxx    ( 2 octet format)
 * 0000 0800-0000 FFFF - 1110xxxx 10xxxxxx 10xxxxxx (3 octet format)
 * (this keeps going for 32 bit unicode) 
 * 
 *
 * Return value:  TRUE, if the text is in UTF-8 format.
 *                FALSE, if the text is not in UTF-8 format.
 *                We will also return FALSE is it is only 7-bit ascii, so the right code page
 *                will be used.
 *
 *                Actually for 7 bit ascii, it doesn't matter which code page we use, but
 *                notepad will remember that it is utf-8 and "save" or "save as" will store
 *                the file with a UTF-8 BOM.  Not cool.
 */

INT IsTextUTF8( LPSTR lpstrInputStream, INT iLen )
{
    INT   i;
    DWORD cOctets;  // octets to go in this UTF-8 encoded character
    UCHAR chr;
    BOOL  bAllAscii= TRUE;

    cOctets= 0;
    for( i=0; i < iLen; i++ ) {
        chr= *(lpstrInputStream+i);

        if( (chr&0x80) != 0 ) bAllAscii= FALSE;

        if( cOctets == 0 )  {
            //
            // 7 bit ascii after 7 bit ascii is just fine.  Handle start of encoding case.
            //
            if( chr >= 0x80 ) {  
               //
               // count of the leading 1 bits is the number of characters encoded
               //
               do {
                  chr <<= 1;
                  cOctets++;
               }
               while( (chr&0x80) != 0 );

               cOctets--;                        // count includes this character
               if( cOctets == 0 ) return FALSE;  // must start with 11xxxxxx
            }
        }
        else {
            // non-leading bytes must start as 10xxxxxx
            if( (chr&0xC0) != 0x80 ) {
                return FALSE;
            }
            cOctets--;                           // processed another octet in encoding
        }
    }

    //
    // End of text.  Check for consistency.
    //

    if( cOctets > 0 ) {   // anything left over at the end is an error
        return FALSE;
    }

    if( bAllAscii ) {     // Not utf-8 if all ascii.  Forces caller to use code pages for conversion
        return FALSE;
    }

    return TRUE;
}

#4


http://blog.csdn.net/fjye/archive/2007/02/02/1501442.aspx

#5


TO:fjye(老姜) 
假设string path="D:\test\test.txt",那么是这样调用你的涵数吗?
int temp=IsTextUTF8(path.c_str(),path.Length());
这样调用后utf8编码的txt文件和非utf8编码的txt文件返回的值都是0。

#6


UTF8/UTF16等编码的文件,主要分为两种,一种是有标识头的,一种是无标识头的,通常在讨论的时候,只讨论有标识头的。先读取前三个字节,判断编码(UTF8三个,UTF16只须两个)。对于只有几个字节的数据,无须采用memcmp,而直接采用等于操作符就可以。由于考虑到文件可能会比较大,以及编码问题,不建议采用Utf8ToAnsi进行转换。最好的办法是MultiByteToWideChar/WideCharToMultiByte进行映视。否则的话会由于当前系统的编码与原始文件内容的编码不一致而导致乱码(比如说一篇GBK码字元组成的文章,被存为UTF8编码文件之后,再转换的时候,如果当前系统默认是BIG5编码,那么将导致部分GBK/Unicode文字在BIG5字符集当中得不到对应而被默认字符如问号等字元代替)。

#7


记得utf-8的txt的头有FF FE两个字符。ascii没有

#8


记得utf-8的txt的头有FF FE两个字符。ascii没有
===============================
错了,这是UTF16

#9


记得unicode的txt的头有FF FE两个字符。ascii没有

#10


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码

#11


EF BB BF 那这个是不是utf-8的呢?
另外bcb里面不是有Utf8ToAnsi()这个函数吗,直接用不行吗

#12


直接用Utf8ToAnsi(),那会把不是Utf8编码的也转换掉了,这样会出错吧?

#13


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码
=================================
不知道有没看过之前所谓“移动”和“联通”这两词关于UTF-8的故事?

#14


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码

#15


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码
====================================================
我现在的问题就是,是Utf8的用Utf8ToAnsi()转成Ansi,对于不是UTF-8编码的,不能用Utf8ToAnsi(),否则文件变空文件了,数据全部丢失了,这样在转换前就必须判断它是否是Utf8编码。现在问题就是不知道怎么判断

#16


to 楼主,给你一个我自己写的判断文件编码格式的函数吧,另外如果UTF8编码文件很短的话可能会误判,这个就是windows下记事本的那个"联通"的bug,不过处理一般的utf8编码已经足够了



BYTE FileType()
{
    const char File_Unicode_Character[2]={0xFF,0xFE};
    const char File_UTF8_Character[3]={0xEF,0xBB,0xBF};
    BYTE bFileType;
    char buff[4]={NULL};
    FILE *fp;
    fp=fopen(PathName.c_str(),"r");
    if(!fp)
        {
        fclose(fp);
        bFileType = Error_File;
        return bFileType;
        }
    fread(buff,1,3,fp);//共读取3个字节
    fclose(fp);
    if(!memicmp(buff,File_UTF8_Character,sizeof(File_UTF8_Character)))
        {
        bFileType = File_UTF8;
        }
    else if(!memicmp(buff,File_Unicode_Character,sizeof(File_Unicode_Character)))
        {
        bFileType = File_Unicode;
        }
    else
        {
        TMemoryStream *ss;
        ss=new  TMemoryStream();
        ss->LoadFromFile(PathName);

        int size=ss->Size;
        unsigned char *p=new char[size+1];
        memset(p,0x00,size+1);
        ss->Read(p,size);

        if(IsTextUTF8(p,size))
            {
            bFileType = File_UTF8_NOBOM;
            }
        else
            {
            bFileType = File_GBK;
            }
        delete []p;
        delete ss;
        }
    return bFileType;
}

#17


另外这个宏定义下
#define Error_File      0
#define File_GBK        1
#define File_Unicode    2
#define File_UTF8       3
#define File_UTF8_NOBOM 4

#18


TO:fjye(老姜) 
你这个函数里好象少了个IsTextUTF8(p,size)吧?

#19


IsTextUTF8(p,size)是直接用你上面哪个INT IsTextUTF8( LPSTR lpstrInputStream, INT iLen )吗?
BYTE FileType()怎么调用啊?我刚试了下返回值怎么是数字呀

#20


MARK

#21


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码
====================================================
我现在的问题就是,是Utf8的用Utf8ToAnsi()转成Ansi,对于不是UTF-8编码的,不能用Utf8ToAnsi(),否则文件变空文件了,数据全部丢失了,这样在转换前就必须判断它是否是Utf8编码。现在问题就是不知道怎么判断
====================================================
Utf8ToAnsi()不去操作文件,如果不是UTF-8编码的,它返回IsEmpty()为true的AnsiString,文件里的内容又不会变.它的输入参数只是一个AnsiString而已.

另外,我认为关于“联通”的BUG任何方法都不能正确判断,因为GB2312的"联通"也是合法的UTF-8编码. 就象上楼说的,很短的文本就是会存在误判的

#22


把联通和移动的故事贴出来看看,加深印象!

#1


关注

#2


UTF8编码其实和Unicode是同类,就是在编码方式上不同。

首先UTF8编码后的大小是不一定,不像Unicode编码后的大小是一样的。Unicode的编码中一个英文字母 “a” 和 一个汉字 “好”,编码后都是占用的空间大小是一样的,都是两个字节。而UTF8编码中一个英文字母“a” 和 一个汉字 “好”,编码后占用的空间大小就不样了,前者是一个字节,后者是三个字节。

UTF8编码的原理:因为一个字母还有一些键盘上的符号加起来只用二进制七位就可以表示出来,而一个字节就是八位,所以UTF8就用一个字节来表式字母和一些键盘上的符号。然而当我们拿到被编码后的一个字节后怎么知道它的组成?它有可能是英文字母的一个字节,也有可能是汉字的三个字节中的一个字节!所以,UTF8是有标志位的。
当要表示的内容是7位的时候就用一个字节:0******* 第一个0为标志位,剩下的空间正好可以表示ASCII码0-127的内容。当要表示的内容在8到11位的时候就用两个字节:110***** 10******第一个字节的110和第二个字节的10为标志位。
当要表示的内容在12到16位的时候就用三个字节:1110***** 10****** 10******和上面一样,第一个字节的1110和第二、三个字节的10都是标志位,剩下的空间正好可以表示汉字。
依次类推:
四个字节:11110**** 10****** 10****** 10****** 
五个字节:111110*** 10****** 10****** 10****** 10****** 
六个字节:1111110** 10****** 10****** 10****** 10****** 10******

#3


/* IsTextUTF8
 *
 * UTF-8 is the encoding of Unicode based on Internet Society RFC2279
 * ( See http://www.cis.ohio-state.edu/htbin/rfc/rfc2279.html )
 *
 * Basicly:
 * 0000 0000-0000 007F - 0xxxxxxx  (ascii converts to 1 octet!)
 * 0000 0080-0000 07FF - 110xxxxx 10xxxxxx    ( 2 octet format)
 * 0000 0800-0000 FFFF - 1110xxxx 10xxxxxx 10xxxxxx (3 octet format)
 * (this keeps going for 32 bit unicode) 
 * 
 *
 * Return value:  TRUE, if the text is in UTF-8 format.
 *                FALSE, if the text is not in UTF-8 format.
 *                We will also return FALSE is it is only 7-bit ascii, so the right code page
 *                will be used.
 *
 *                Actually for 7 bit ascii, it doesn't matter which code page we use, but
 *                notepad will remember that it is utf-8 and "save" or "save as" will store
 *                the file with a UTF-8 BOM.  Not cool.
 */

INT IsTextUTF8( LPSTR lpstrInputStream, INT iLen )
{
    INT   i;
    DWORD cOctets;  // octets to go in this UTF-8 encoded character
    UCHAR chr;
    BOOL  bAllAscii= TRUE;

    cOctets= 0;
    for( i=0; i < iLen; i++ ) {
        chr= *(lpstrInputStream+i);

        if( (chr&0x80) != 0 ) bAllAscii= FALSE;

        if( cOctets == 0 )  {
            //
            // 7 bit ascii after 7 bit ascii is just fine.  Handle start of encoding case.
            //
            if( chr >= 0x80 ) {  
               //
               // count of the leading 1 bits is the number of characters encoded
               //
               do {
                  chr <<= 1;
                  cOctets++;
               }
               while( (chr&0x80) != 0 );

               cOctets--;                        // count includes this character
               if( cOctets == 0 ) return FALSE;  // must start with 11xxxxxx
            }
        }
        else {
            // non-leading bytes must start as 10xxxxxx
            if( (chr&0xC0) != 0x80 ) {
                return FALSE;
            }
            cOctets--;                           // processed another octet in encoding
        }
    }

    //
    // End of text.  Check for consistency.
    //

    if( cOctets > 0 ) {   // anything left over at the end is an error
        return FALSE;
    }

    if( bAllAscii ) {     // Not utf-8 if all ascii.  Forces caller to use code pages for conversion
        return FALSE;
    }

    return TRUE;
}

#4


http://blog.csdn.net/fjye/archive/2007/02/02/1501442.aspx

#5


TO:fjye(老姜) 
假设string path="D:\test\test.txt",那么是这样调用你的涵数吗?
int temp=IsTextUTF8(path.c_str(),path.Length());
这样调用后utf8编码的txt文件和非utf8编码的txt文件返回的值都是0。

#6


UTF8/UTF16等编码的文件,主要分为两种,一种是有标识头的,一种是无标识头的,通常在讨论的时候,只讨论有标识头的。先读取前三个字节,判断编码(UTF8三个,UTF16只须两个)。对于只有几个字节的数据,无须采用memcmp,而直接采用等于操作符就可以。由于考虑到文件可能会比较大,以及编码问题,不建议采用Utf8ToAnsi进行转换。最好的办法是MultiByteToWideChar/WideCharToMultiByte进行映视。否则的话会由于当前系统的编码与原始文件内容的编码不一致而导致乱码(比如说一篇GBK码字元组成的文章,被存为UTF8编码文件之后,再转换的时候,如果当前系统默认是BIG5编码,那么将导致部分GBK/Unicode文字在BIG5字符集当中得不到对应而被默认字符如问号等字元代替)。

#7


记得utf-8的txt的头有FF FE两个字符。ascii没有

#8


记得utf-8的txt的头有FF FE两个字符。ascii没有
===============================
错了,这是UTF16

#9


记得unicode的txt的头有FF FE两个字符。ascii没有

#10


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码

#11


EF BB BF 那这个是不是utf-8的呢?
另外bcb里面不是有Utf8ToAnsi()这个函数吗,直接用不行吗

#12


直接用Utf8ToAnsi(),那会把不是Utf8编码的也转换掉了,这样会出错吧?

#13


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码
=================================
不知道有没看过之前所谓“移动”和“联通”这两词关于UTF-8的故事?

#14


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码

#15


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码
====================================================
我现在的问题就是,是Utf8的用Utf8ToAnsi()转成Ansi,对于不是UTF-8编码的,不能用Utf8ToAnsi(),否则文件变空文件了,数据全部丢失了,这样在转换前就必须判断它是否是Utf8编码。现在问题就是不知道怎么判断

#16


to 楼主,给你一个我自己写的判断文件编码格式的函数吧,另外如果UTF8编码文件很短的话可能会误判,这个就是windows下记事本的那个"联通"的bug,不过处理一般的utf8编码已经足够了



BYTE FileType()
{
    const char File_Unicode_Character[2]={0xFF,0xFE};
    const char File_UTF8_Character[3]={0xEF,0xBB,0xBF};
    BYTE bFileType;
    char buff[4]={NULL};
    FILE *fp;
    fp=fopen(PathName.c_str(),"r");
    if(!fp)
        {
        fclose(fp);
        bFileType = Error_File;
        return bFileType;
        }
    fread(buff,1,3,fp);//共读取3个字节
    fclose(fp);
    if(!memicmp(buff,File_UTF8_Character,sizeof(File_UTF8_Character)))
        {
        bFileType = File_UTF8;
        }
    else if(!memicmp(buff,File_Unicode_Character,sizeof(File_Unicode_Character)))
        {
        bFileType = File_Unicode;
        }
    else
        {
        TMemoryStream *ss;
        ss=new  TMemoryStream();
        ss->LoadFromFile(PathName);

        int size=ss->Size;
        unsigned char *p=new char[size+1];
        memset(p,0x00,size+1);
        ss->Read(p,size);

        if(IsTextUTF8(p,size))
            {
            bFileType = File_UTF8_NOBOM;
            }
        else
            {
            bFileType = File_GBK;
            }
        delete []p;
        delete ss;
        }
    return bFileType;
}

#17


另外这个宏定义下
#define Error_File      0
#define File_GBK        1
#define File_Unicode    2
#define File_UTF8       3
#define File_UTF8_NOBOM 4

#18


TO:fjye(老姜) 
你这个函数里好象少了个IsTextUTF8(p,size)吧?

#19


IsTextUTF8(p,size)是直接用你上面哪个INT IsTextUTF8( LPSTR lpstrInputStream, INT iLen )吗?
BYTE FileType()怎么调用啊?我刚试了下返回值怎么是数字呀

#20


MARK

#21


直接用Utf8ToAnsi(),如里返回空值就不是UTF-8编码
====================================================
我现在的问题就是,是Utf8的用Utf8ToAnsi()转成Ansi,对于不是UTF-8编码的,不能用Utf8ToAnsi(),否则文件变空文件了,数据全部丢失了,这样在转换前就必须判断它是否是Utf8编码。现在问题就是不知道怎么判断
====================================================
Utf8ToAnsi()不去操作文件,如果不是UTF-8编码的,它返回IsEmpty()为true的AnsiString,文件里的内容又不会变.它的输入参数只是一个AnsiString而已.

另外,我认为关于“联通”的BUG任何方法都不能正确判断,因为GB2312的"联通"也是合法的UTF-8编码. 就象上楼说的,很短的文本就是会存在误判的

#22


把联通和移动的故事贴出来看看,加深印象!