程序读取文本文件之UTF8-BOM

一楼放图

在这里插入图片描述
在这里插入图片描述
以上是用java做一个简单的文件读取,在TXT中,我们的第一个字明明是A,但是打印的结果,确是一个问号。

发现问题

首先文件是UTF8格式,我们确定读取时候设置的格式无错,那我们检查一下NOTEPAD++文本编辑器中,的编码格式。显示当前文本格式为UTF8,
在这里插入图片描述
当然我们还发现,里面有一个选项是以UTF8-无BOM格式编码。经过尝试,当我们文件以UTF8无BOM时候编码时候,文件读取结果正常。那么我们可以肯定,问题是处在这个BOM上。那么我们对BOM到底是干啥的,给一个详细的解释。

编码格式说明

首先明确一下BOM-即BYTE ORDER MARK,就是字节的顺序,那么我们常见的UTF8格式,他是以1个字节为单元进行数据存储的,那么我们想知道下一位的时候,只要当前地址+1,即可。
而UTF16,UTF32分别是以2个和4个字节为单元进行数据存储的。这样在传输或者存储过程中,我们就要考虑字节的顺序问题,很显然,UTF16,UTF32在这一点上有区别于UTF8。
那么在计算机处理文本的时候,他要知道这个文件到底是UTF8,还是UTF16还是UTF32,来确定每次的读取单元和顺序。所以就不得不给UTF8前面也加个帽子。
其实这个BOM对UTF8什么用都没有,因为一次默认读1个单元,又与顺序无关。

代码说明

接下来用过代码来更好的解释一下,计算机的读取过程。
首先,BOM有4byte,每个byte都可对标记进行存储。

package com.sunreal.test;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackInputStream;
import java.io.Reader;

public class UnicodeReader extends Reader {
	PushbackInputStream internalIn;
	InputStreamReader internalIn2 = null;
	String defaultEnc;

	private static final int BOM_SIZE = 4;

	UnicodeReader(InputStream in, String defaultEnc) {
		internalIn = new PushbackInputStream(in, BOM_SIZE);
		this.defaultEnc = defaultEnc;
	}

	UnicodeReader(InputStream in) {
		internalIn = new PushbackInputStream(in, BOM_SIZE);
	}

	public String getDefaultEncoding() {
		return defaultEnc;
	}

	public String getEncoding() {
		if (internalIn2 == null)
			return null;
		return internalIn2.getEncoding();
	}

	protected void init() throws IOException {
		if (internalIn2 != null)
			return;

		String encoding;
		byte bom[] = new byte[BOM_SIZE];
		int n, unread;
		n = internalIn.read(bom, 0, bom.length);

		if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
			encoding = "UTF-32BE";
			unread = n - 4;
		} else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00)
				&& (bom[3] == (byte) 0x00)) {
			encoding = "UTF-32LE";
			unread = n - 4;
		} else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {
			encoding = "UTF-8";
			unread = n - 3;
		} else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
			encoding = "UTF-16BE";
			unread = n - 2;
		} else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
			encoding = "UTF-16LE";
			unread = n - 2;
		} else {
			// Unicode BOM mark not found, unread all bytes
			encoding = defaultEnc;
			unread = n;
		}
		// System.out.println("read=" + n + ", unread=" + unread);

		if (unread > 0)
			internalIn.unread(bom, (n - unread), unread);

		// Use given encoding
		if (encoding == null) {
			internalIn2 = new InputStreamReader(internalIn);
		} else {
			internalIn2 = new InputStreamReader(internalIn, encoding);
		}
	}

	public void close() throws IOException {
		init();
		internalIn2.close();
	}

	public int read(char[] cbuf, int off, int len) throws IOException {
		init();
		return internalIn2.read(cbuf, off, len);
	}

}

我们来看一下这块的核心代码,先哪UTF-8来说,
如果byte0=EF,byte1=BB,byte2=BF,那么表示的就是UTF-8,按照代码路径,我们从n-3开始读取。也就是计算机发现BOM位是EFBBBF,那么就从第二位开始读,并且知道他是UTF8格式,但是比如我们的文件本身就是UTF8编码的,这个BOM位就毫无意义了,从开始的截图看就是正好去掉了A前面那个?,当然这里也给出来UTF16等其他的处理方式。

然后我们以后万金油的处理UTF-?类型的 文本文件,只要使用上来截图的第二部分的代码即可,他会自动根据文件格式确定读取方式。为了方便复制,贴以下代码。

File f=new File("D:\\unitfee_159901_20200714150101_001.txt");
BufferedReader br = new BufferedReader(new UnicodeReader(new FileInputStream(f),Charset.defaultCharset().name()));
            String line = null;
            while((line=br.readLine()) != null){
            	System.out.println(line);
            }

后记

纯粹上从技术角度解决问题,上述代码没有错,但是这只是代表可以这样处理,再我们给别人提供文本信息的时候,大家一定注意,要给别人UTF8无BOM的,因为无BOM计算机会自动按顺序读取,每次地址+1.省去不必要的麻烦。而我们写代码与其他软件进行文本交互的时候,也尽量生成UTF8无BOM的。
最后,本方法同样适用于常见的XML文件的处理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值