UTF8和GBK区别,以及读取文件存在BOM,第一个字符值为65279

字符编码解析
本文详细介绍了GBK、UTF-8和ISO-8859-1等字符编码方式的区别,包括各自的适用场景及占用空间大小;解释了BOM的作用及在不同编码下的表现形式;通过示例代码展示了如何读取不同编码的文件;并给出了在HTTP请求参数中处理中文字符的具体步骤。

一、GBK的文字编码:是双字节来表示的,即不论中、英文字符均使用双字节来表示,只不过为区分中文,将其最高位都定成1。

UTF-8编码:则是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码。对于英文字符较多的网站则用UTF-8节省空间。

所以如果是UTF8编码,则在外国人的英文IE上也能显示中文,而无需他们下载IE的中文语言支持包。 所以,对于英文比较多的论坛 ,使用GBK则每个字符占用2个字节,而使用UTF-8英文却只占一个字节。

ISO-8859-1:中文只占一个字节

 

二、首先了解一下BOM,字节顺序标记(英语:byte-order mark,BOM)是位于码点U+FEFF的统一码字符的名称。当以UTF-16或UTF-32来将UCS/统一码字符所组成的字符串编码时,这个字符被用来标示其字节序。它常被用来当做标示文件是以UTF-8、UTF-16或UTF-32编码的记号。【来自维基百科

 

读取文件代码,代码底层读取文件时,会根据不同编码的CharsetDecoder实现类来读取(比如UTF8编码):

 public static void main(String[] args) throws FileNotFoundException {
		FileReader reader = new FileReader(new File("data.txt"));
		int read;
		try {
			System.out.println(reader.getEncoding());
			while((read = reader.read())!=-1){
				System.out.println(read+":"+(char)(read));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

 

新建文本,内容为:

1411111哈哈

 1、当格式为以UTF8有BOM时:

输入结果:

UTF8
65279:
49:1
52:4
49:1
49:1
49:1
49:1
49:1
21704:哈
21704:哈

 

2、当格式为以UTF8无BOM时:

输入结果:

UTF8
49:1
52:4
49:1
49:1
49:1
49:1
49:1
21704:哈
21704:哈

 

注意:上面代码中,我们直接用char强转读取的int值,并不是因为不考虑ASCII码表,而是因为read()方法底层读取的是char字段,直接赋值给int返回的。

int read;
System.out.println(reader.getEncoding());
while((read = reader.read())!=-1){
	System.out.println(read+":"+(char)(read));
}
 read()方法,arg1[0]为char类型:
private int read0() throws IOException {
      Object arg0 = this.lock;
      synchronized(this.lock) {
         if(this.haveLeftoverChar) {
            this.haveLeftoverChar = false;
            return this.leftoverChar;
         } else {
            char[] arg1 = new char[2];
            int arg2 = this.read(arg1, 0, 2);
            switch(arg2) {
            case -1:
               return -1;
            case 0:
            default:
               assert false : arg2;

               return -1;
            case 2:
               this.leftoverChar = arg1[1];
               this.haveLeftoverChar = true;
            case 1:
               return arg1[0];
            }
         }
      }
   }
 三、一个ASCⅡ占用一个字节,所以假如在操作二进制流,比如发送http请求组装参数时,你把内容放入ByteArrayOutputStream,这时候你需要输入"xxxx?myname=liu刘",则操作如下示例:
os.write(URLEncoder.encode("xxxx", requestEncoding).getBytes());
os.write(63);//ASCⅡ代表?
os.write(URLEncoder.encode("myname", requestEncoding).getBytes());
os.write(61);//ASCⅡ代表=
os.write(URLEncoder.encode("liu刘", requestEncoding).getBytes());
其中用的URLEncoder编解码目的是因为http的get请求不支持中文,所以先编码一下,接受后解码可以预防乱码问题,英文编码前后不变化。示例:
		String content = "abc123ha刘";
		String encoderString = URLEncoder.encode(content, "utf-8");
		System.out.println(encoderString);
		String decodedString = URLDecoder.decode(encoderString, "utf-8");
		System.out.println(decodedString);
 输出:
abc123ha%E5%88%98
abc123ha刘
 
四、
 
<think>我们正在使用Qt(C++)来获取文件的编码格式,包括GBKUTF-8UTF-8BOM、ANSI(在中文环境下通常指GBK)等。 根据引用[1],Qt内部使用UTF-8编码(Qt5/Qt4),但文件可能有不同的编码格式。我们需要检测文件的编码。 思路: 1. 检测BOM(Byte Order Mark): 如果文件开头有BOM,则可以根据BOM判断编码。 - UTF-8BOM: 前三个字节是0xEF, 0xBB, 0xBF。 - UTF-16(大端): 前两个字节是0xFE 0xFF。 - UTF-16(小端): 前两个字节是0xFF 0xFE。 - 注意:ANSI(GBKUTF-8不带BOM没有BOM头。 2. 如果没有BOM,则需要通过分析内容来判断。常见方法是尝试用UTF-8解码,如果成功且没有无效字符,则可能是UTF-8,否则可能是ANSI(GBK等)。但需要注意,纯ASCII文件UTF-8ANSI都能解码,因此通常默认为UTF-8(不带BOM)或者系统编码(如GBK)。 3. 在Windows中文系统中,ANSI编码即GBK。所以我们需要区分:带BOMUTF-8、不带BOMUTF-8GBK(即ANSI)等。 然而,Qt本身没有直接提供检测文件编码的函数。我们可以采用以下步骤: 步骤: a. 读取文件的前几个字节(比如3个字节)检查BOM。 b. 如果有BOM,则返回对应的编码。 c. 如果没有BOM,尝试将整个文件内容用UTF-8解码。如果成功(即没有无效序列),则认为是UTF-8(不带BOM);否则,使用本地编码(如GBK)来解码。 但是,这里有一个问题:用UTF-8解码“成功”并不一定意味着文件就是UTF-8编码,因为GBK编码的字节序列也可能恰好符合UTF-8的编码规则(虽然概率较低)。因此,这种方法并不完全可靠。更可靠的方式是使用编码检测库,如ICU或QTextCodec提供的启发式方法。不过,在Qt中,我们可以使用QTextCodec来尝试检测。 在Qt5中,QTextCodec类已经被标记为过时(deprecated),但在Qt5.15之前仍可用。在Qt6中,QTextCodec被移除了。因此,我们需要考虑Qt版本。 不过,我们也可以自己实现一个简单的检测: 具体方法: 1. 读取文件为二进制数据(QByteArray)。 2. 检查BOM,如果存在则返回相应类型。 3. 如果没有BOM,尝试用UTF-8解码。由于UTF-8有严格的格式,如果遇到非法UTF-8序列,则认为不是UTF-8。 4. 如果UTF-8解码成功,则认为是UTF-8(不带BOM)。 5. 如果UTF-8解码失败,则使用本地编码(如GBK)来解码,并认为它是ANSI(即GBK)。 但是,注意:有些文件可能是其他编码(如ISO-8859-1),但我们这里主要处理中文,所以主要考虑UTF-8GBK。 另外,我们还需要考虑一种情况:文件可能完全是ASCII(即所有字节都小于0x80),那么它既是有效的UTF-8(不带BOM)也是有效的ANSI,这种情况下,我们可以认为它是UTF-8(不带BOM),因为UTF-8是ASCII兼容的。 实现代码(Qt5): 注意:由于Qt6移除了QTextCodec,如果需要在Qt6中使用,需要使用其他方法。但根据问题,我们使用Qt4/Qt5,所以可以用QTextCodec。 使用QTextCodec::codecForUtfText()可以检测BOM,但如果没有BOM,它返回本地编码(可能是系统默认编码)。所以这个方法只能检测带BOMUTF编码。 因此,我们需要自己实现: 示例代码: ```cpp #include <QFile> #include <QTextCodec> #include <QDebug> // 定义编码类型枚举 enum Encoding { ANSI, // GBK in Chinese environment UTF8, UTF8_BOM, UTF16_LE, UTF16_BE }; Encoding detectEncoding(const QString &filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { // 无法打开文件,返回默认编码,或者抛出错误 return ANSI; } // 读取前3个字节(用于检查BOM) QByteArray data = file.read(3); file.close(); // 先关闭,后面再打开 // 检查BOM if (data.size() >= 3) { if (data[0] == '\xEF' && data[1] == '\xBB' && data[2] == '\xBF') { return UTF8_BOM; } } if (data.size() >= 2) { if (data[0] == '\xFF' && data[1] == '\xFE') { return UTF16_LE; } else if (data[0] == '\xFE' && data[1] == '\xFF') { return UTF16_BE; } } // 如果没有BOM,则读取整个文件内容(注意:大文件可能影响性能,可以只读取部分) if (!file.open(QIODevice::ReadOnly)) return ANSI; data = file.readAll(); file.close(); // 检查是否为有效的UTF-8(不带BOM) QTextCodec::ConverterState state; QTextCodec *codec = QTextCodec::codecForName("UTF-8"); const QString text = codec->toUnicode(data.constData(), data.size(), &state); if (state.invalidChars == 0) { // 没有无效字符,认为是UTF-8(不带BOM) return UTF8; } // 否则认为是ANSI(在中文系统中为GBK) return ANSI; } ``` 然而,上面的方法有一个问题:对于大文件读取整个文件可能会慢。我们可以只读取一部分(例如1024字节)来检测,因为如果文件开头部分不是有效的UTF-8,那么整个文件就不是UTF-8(除非后面有非UTF-8字符而我们没读到,但这样检测就不准确),所以权衡下,可以读取整个文件,但如果是大文件,我们也可以只读一部分。 改进:为了提高性能,我们可以只读取文件的一部分(比如前1KB)进行检测。因为如果文件很大,而开头部分已经出现了无效的UTF-8序列,那么就可以判断它不是UTF-8。但是,如果文件开头是有效的UTF-8,但后面有无效序列,那么我们就可能误判。不过,这种情况很少见,因为通常一个文件都是一种编码。另外,我们也可以选择读取更多内容,但折中方案是读取前4096字节。 修改代码(只读取前4096字节): ```cpp // 如果没有BOM,则读取一部分内容(例如4096字节) const qint64 maxSize = 4096; data = file.read(maxSize); // 然后检测UTF-8有效性 ``` 注意:ANSI编码(GBK)是双字节编码(对于中文),所以如果只读一部分,可能会截断一个中文字符,导致最后两个字节可能不完整。但是,由于我们检测UTF-8时,只需要检查每个字节是否符合UTF-8编码规则(不依赖于字符的完整性),所以即使截断,也只影响最后一个可能的字符,而前面的字节已经足够判断是否有无效序列。因此,我们可以忽略截断问题。 另外,我们还需要考虑纯ASCII的情况,它会被检测为UTF-8,这是符合预期的。 总结: 我们通过以下步骤检测编码: 1. 检查BOMUTF-8BOMUTF-16 LE/BE) 2. 如果没有BOM,检查内容是否是有效的UTF-8(通过尝试转换并检查无效字符) 3. 如果有效,返回UTF-8(不带BOM);否则返回ANSI(在中文系统中即GBK) 对于UTF-16,我们只通过BOM判断,因为如果没有BOM,检测会比较复杂(需要判断字节对是否符合UTF-16的编码规则),而且问题中主要要求GBKUTF-8,所以我们暂时不考虑无BOMUTF-16。 因此,上面的代码只处理了带BOMUTF-16。如果遇到不带BOMUTF-16,会被当作ANSI(然后用GBK解码会乱码)。所以如果需要支持不带BOMUTF-16,需要另外检测。但是,根据引用,在中文Windows中,ANSI就是GBK,而UTF-16通常用于内部Windows API(Unicode),所以文件存储较少使用UTF-16。因此,我们按照需求,主要关注GBKUTF-8。 另外,ANSI在Windows中文环境下是GBK,但在其他语言环境下不同。所以,我们这里假设系统是中文环境。 最后,我们返回的编码类型是一个枚举,用户可以根据这个编码类型来打开文件。 使用示例: ```cpp Encoding encoding = detectEncoding("test.txt"); QTextCodec *codec = nullptr; switch (encoding) { case UTF8_BOM: // 注意:带BOMUTF-8,在读取时跳过BOM,或直接使用UTF-8编解码器(会自动跳过BOM) codec = QTextCodec::codecForName("UTF-8"); break; case UTF8: codec = QTextCodec::codecForName("UTF-8"); break; case UTF16_LE: codec = QTextCodec::codecForName("UTF-16LE"); break; case UTF16_BE: codec = QTextCodec::codecForName("UTF-16BE"); break; case ANSI: // 在中文Windows系统中,ANSI就是GBK codec = QTextCodec::codecForName("GBK"); break; } if (codec) { QFile file("test.txt"); if (file.open(QIODevice::ReadOnly)) { // 注意:如果检测到BOM,我们已经在前面判断了,但是这里使用codec来读取会自动处理BOM(QTextStream会处理) // 但我们也可以自己处理:对于UTF8_BOM文件开头有3个字节的BOM,需要跳过?实际上,用UTF-8编解码器时,它会自动忽略BOM(不会把它当作内容)。所以直接读取即可。 QTextStream ts(&file); ts.setCodec(codec); QString content = ts.readAll(); file.close(); } } ``` 请注意:上面的代码中,对于UTF-8BOM,我们使用同样的UTF-8编解码器,因为QTextCodec在解码时会自动忽略BOM(如果存在)。同样,对于UTF-16,QTextCodec也会自动处理BOM。 但是,在检测函数中,我们已经读取文件的前几个字节(检查BOM),然后关闭了文件。在后续打开文件读取全部内容时,文件指针在开头,所以读取全部内容时会包含BOM。而QTextStream在设置编解码器后,会自动跳过BOM(对于UTF-8,它会跳过开头的BOM;对于UTF-16,也会跳过BOM)。所以不需要额外跳过。 但是,如果我们自己用QFile读取全部内容,然后用QTextCodec::toUnicode()转换,那么对于带BOMUTF-8,我们需要手动跳过前三个字节吗?实际上,QTextCodec::toUnicode()不会自动跳过BOM(除非编解码器内部处理)。因此,在检测函数中,我们读取了前3个字节(用于检查BOM),然后关闭了文件。在后续读取整个文件时,我们重新打开,所以文件指针在开头,因此整个文件内容(包括BOM)都会被读入。而在转换时,如果我们用UTF-8编解码器,它不会跳过BOM,因为该编解码器并不认为BOM是特殊的。所以,在转换后,字符串开头会有一个U+FEFF(零宽空格)。这通常不是我们想要的。 解决: 在检测到UTF-8_BOM时,我们可以手动移除BOM(即前三个字节),然后再用UTF-8编解码器转换。或者,在后续读取文件时,如果是带BOM的编码,则跳过这些字节。 但是,在QTextStream中,它会自动处理BOM读取时跳过,写入时添加)。因此,我们建议使用QTextStream来读取文件,这样就不会出现BOM出现在文本内容中的情况。 所以,在读取文件时,我们使用QTextStream并设置编解码器,然后读取,这样BOM会被自动处理(跳过)。 因此,上面的使用示例中的方式(通过QTextStream读取)是合适的。 总结:我们提供了一个检测文件编码的函数,并给出了如何读取文件的方法。 注意:这种方法对于UTF-8的检测并不100%准确(因为可能存在GBK编码的字节序列恰好符合UTF-8规则),但在实际应用中,这种情况很少,尤其是中文字符GBK中通常两个字节都是大于0x80的,而UTF-8的中文字符是3个字节,所以规则不同,冲突的概率较低。 如果要求更高的准确性,可以使用一些统计方法(如chardet库),但Qt本身没有提供。 因此,我们提供上述方法供参考。 相关问题: 1. 在Qt中如何正确处理不同编码的文本文件? 2. 如何在没有BOM的情况下更准确地检测文件的编码? 3. 在Qt6中,由于移除了QTextCodec,如何实现文件编码的检测? 请根据实际需要选择解决方案。 </think>在Qt中检测文件编码格式(包括GBKUTF-8UTF-8BOM、ANSI)可通过以下方法实现: ### 核心方法(Qt5/C++) ```cpp #include <QFile> #include <QTextCodec> #include <QByteArray> QString detectFileEncoding(const QString& filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) return "Open failed"; // 1. 检查BOM头 QByteArray bom = file.read(4); if (bom.startsWith("\xEF\xBB\xBF")) // UTF-8 BOM return "UTF-8 with BOM"; if (bom.startsWith("\xFF\xFE")) // UTF-16 LE return "UTF-16 LE"; if (bom.startsWith("\xFE\xFF")) // UTF-16 BE return "UTF-16 BE"; // 2. 无BOM时检测编码 file.seek(0); QByteArray data = file.readAll(); // UTF-8有效性检测(严格模式) QTextCodec::ConverterState state; QTextCodec *utf8Codec = QTextCodec::codecForName("UTF-8"); utf8Codec->toUnicode(data.constData(), data.size(), &state); if (state.invalidChars == 0) return "UTF-8"; // 3. 中文环境默认GBK(ANSI) // 注意:纯英文文件可能同时符合GBKUTF-8 return "GBK/ANSI"; // 简体中文Windows环境 } ``` ### 关键原理说明 1. **BOM检测**(优先处理) - UTF-8BOM:开头字节 `EF BB BF` - UTF-16:开头 `FF FE`(LE)或 `FE FF`(BE) 2. **无BOM时的UTF-8检测** - 使用 `QTextCodec::ConverterState` 统计无效字符 - 若无效字符为0,则判定为UTF-8 3. **ANSI/GBK判定** - 在简体中文Windows中,ANSI即GBK编码 - 若无BOM且非UTF-8,默认返回GBK(因系统环境而异) ### 使用示例 ```cpp int main() { QString result = detectFileEncoding("test.txt"); qDebug() << "Encoding:" << result; // 实际读取时指定编解码器 QFile file("test.txt"); file.open(QIODevice::ReadOnly); QTextStream stream(&file); stream.setCodec(result.toUtf8()); // 设置检测到的编码 QString content = stream.readAll(); } ``` ### 编码转换规则 根据引用[^1]的描述,编码转换需通过Unicode中转: ```mermaid graph LR A[GBK/GB2312] --> B(Unicode) B --> C(UTF-8) C --> B B --> A ``` ### 注意事项 1. **可靠性限制**: - 纯英文文件可能被误判为UTF-8(因兼容ASCII) - 建议优先处理已知BOM的情况 2. **性能优化**: -文件可只读取前1KB内容检测 - 使用 `ConverterState` 避免完整解码开销 3. **系统依赖**: - ANSSI编码在不同语言环境对应不同编码(中文=GBK,日文=Shift_JIS)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值