二进制文件与文本文件的区别【字符集Charset】

计算机上存储的文件在比特位上都是以二进制数字0或1表示,因此在物理层面上,文本文件和二进制文件没有本质差异,都是由数字0或1组成的比特位集合。

文本文件和二进制文件,两者的差异体现在编码逻辑,需要根据文件头中标记来区分。

文本文件是基于字符编码的文件,通常文件头有"字节序标记"BOM。什么是BOM?

不同的字符编码方案有固定的BOM标记。文本读写应用程序通常是根据BOM来确认文本的编码格式,然后才能处理文件中的信息。

二进制文件由二进制数字0和1组成,不同应用有不同的编码方案,通常也有文件头信息。。例如,音频、视频和图像文件都属于二进制文件,它们也各有编码方案,需要专用程序来处理。有的二进制文件也有"字节序标记"BOM,其作用主要为了确认编码是大端还是小端,用于确认编码方案中每组字节的编码排列顺序。

比如 bmp文件,它的文件头信息,前2个字节表示文件格式为BMP格式,接着的 8个字节表示文件的长度,再接着的4个字节表示 bmp文件头的长度。然后再根据BMP文件的编码方案可以解释出绘制图像。

win10系统文本文件的编码方案

win10系统默认的字符集(Charset)是GBK。

我们可用下面的Java程序来打印win10系统默认的字符集,以及支持的字符集:

cpp 复制代码
/***
 * @author QiuGen
 * @description  系统默认字符集(Charset)打印例程CharsetPrn.java
 * @date 2024/9/16
 * ***/
import java.nio.charset.Charset;	//程序CharsetPrn.java开始
import java.util.SortedMap;
public class CharsetPrn {
	public static void main(String[] args) {
		Charset charset = Charset.defaultCharset(); //获取系统环境的默认字符集名
		System.out.println("当前系统环境的默认字符集名称");
		System.out.println(charset);
		System.out.println("当前系统环境的可用字符集名称");
		SortedMap<String,Charset> map = Charset.availableCharsets();
		map.forEach((k,v)->System.out.println(k));
	}
}

在win10系统下的字符编码方案有以下几种,ASCII,ANSI、UTF-8、带有BOM的UTF-8 、UTF-16LE、UTF-16BE。

win10系统默认的字符集(Charset)编码方案是GBK编码。

这几种编码的特点:

(1) ANSI和ASCII编码: ASCII编码是ANSI编码的子集。ANSI编码是兼容ASCII编码的,如果字节的最高位是0(0-7F),二进制形如0XXX XXXX,表达的是ASCII字符。如果字节的最高位是1(80-FE),则是ANSI(GBK)编码,注意,这时候是两个字节表达一个汉字,也就是说两个字节的最高位都是1的字节代表一个汉字,二进制形如1XXX XXXX, 1XXX XXXX 。

在ANSI编码下,如果文本字符都是西文字符,则也可以认为是ASCII编码,当有连续高为是1的字节串出现的时候,这是可以判定为ANSI或GBK编码。GBK编码一定是两个高位均为1的字节表达一个汉字。ANSI每个字节的高位可以是1或0,若字节高位是0,则一个字节表达一个ASCII字符。

(2) UTF-8编码:utf-8是一种多字节编码的字符集,表示一个Unicode字符时,它可以是1个至多个字节。即在文本全部是ASCII字符时utf-8是和ASCII一致的(utf-8向下兼容ASCII)。最多6个字节表达一个字符,utf-8字节编码如下所示:

1字节:0xxxxxxx

2字节:110xxxxx 10xxxxxx

3字节:1110xxxx 10xxxxxx 10xxxxxx ,一般汉字用这个3字节表达

4字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

5字节:111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

注意,在UTF-8编码中,多种长度是混合存在的,一个字符串可能有1,2,...,6个字节来表示的字符同时存在 。因此UTF-8编码太复杂,效率较低。

(3)有"字节序标记"BOM的UTF-8,其文本文件的头部带有"字节序标记"BOM:0xEF, 0xBB,0xBF,通过判断这个标志,可以判断出这个文本文件是UTF-8编码。

(4)UTF-16LE,字节序是little endian ,是双字节等长编码。文本文件头部带有"字节序标记"BOM:0xFF 0xFE,通过判断这个标志,可以判断出这个文本文件是UTF-16LE编码。

(5) UTF-16BE, 字节序是big endian,是双字节等长编码。文本文件头部带有"字节序标记"BOM:0xFE 0xFF,通过判断这个标志,可以判断出这个文本文件是UTF-16BE编码。

下面是一个根据"字节序标记"BOM测试文本文件编码方案的例程:

cpp 复制代码
/***
 * @author QiuGen
 * @description  根据BOM测试文本文件编码方案例程
 * FileCharsetDetector.java
 * @date 2024/9/16
 * ***/
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class FileCharsetDetector {

	public static void main(String[] args) {
        String filePath = "D:/temp/测试文档2.txt";
        
        try (FileInputStream in = new FileInputStream(filePath)) {
            Charset charset = null;
            int bom[] = new int[3];
            bom[0] = in.read(); //读第1个字节
            bom[1] = in.read(); //读第2个字节
            bom[2] = in.read(); //读第3个字节
            //打印BOM
            System.out.println("BOM:"+Integer.toHexString(bom[0])+Integer.toHexString(bom[1])+Integer.toHexString(bom[2]));
            if (bom[0] == 0xFE && bom[1] == 0xFF) {
                charset = StandardCharsets.UTF_16BE;
            } else if (bom[0] == 0xFF && bom[1] == 0xFE) {
                charset = Charset.forName("UTF-16LE");
            } else if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) {
                charset = StandardCharsets.UTF_8;
            } else {
                charset = Charset.forName("GBK");
            }
            
            System.out.println("文件字符编码: " + charset.name());
        } catch (IOException e) {
            e.printStackTrace();
        }
	}

}

在win10环境,使用文本编辑器应用程序"记事本",分别编写四个文件分别保存为UTF-16LE、 UTF-16BE、UTF-8和ANSI编码格式的文本。进行测试,UTF-16LE和 UTF-16BE能准确打印出BOM,下图是UTF-16LE的测试结果。

UTF-8和ANSI编码格式的文本,测试结果一样,如下所示:

由此说明Win10的UTF-8文本文件文件头中没有标准的"字节序标记"BOM。

相关推荐
gadiaola2 分钟前
【JavaSE面试篇】Java集合部分高频八股汇总
java·面试
艾迪的技术之路24 分钟前
redisson使用lock导致死锁问题
java·后端·面试
今天背单词了吗98042 分钟前
算法学习笔记:8.Bellman-Ford 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·后端·算法·最短路径问题
天天摸鱼的java工程师1 小时前
使用 Spring Boot 整合高德地图实现路线规划功能
java·后端
东阳马生架构1 小时前
订单初版—2.生单链路中的技术问题说明文档
java
咖啡啡不加糖1 小时前
暴力破解漏洞与命令执行漏洞
java·后端·web安全
风象南1 小时前
SpringBoot敏感配置项加密与解密实战
java·spring boot·后端
DKPT1 小时前
Java享元模式实现方式与应用场景分析
java·笔记·学习·设计模式·享元模式
Percep_gan2 小时前
idea的使用小技巧,个人向
java·ide·intellij-idea