Java如何识别上传⽂件的编码,BOM头⼜是什么?
背景:
最近在做⼀个关于上传⽂件,需要识别⽂件编码的场景需求,项⽬中使⽤
org.springframework.web.multipartmons.CommonsMultipartFile来接收上传上件对象,此对象并没有类似getFileCharset()等相关的获取⽂件编码的API。因此,在⽹上补了⼀下功课,了解⼀下,识别⽂件编码的常⽤⽅案,总结于此,以备后查,仅供参考。
⼀、BOM是什么
BOM,即字节顺序标记(ByteOrderMark)是⽤来判断⽂本⽂件是哪⼀种Unicode编码的标记,其本⾝是⼀个Unicode字符("\uFEFF"),位于⽂本⽂件头部。
BOM头是微软公司为了解决不同的编码冲撞问题引⼊的。⽐如,当你新建⼀个⽂本⽂档之后,在⾥⾯输⼊ “联通” 两个字,然后保存。当你再次打开的时候,原来输⼊的 “联通” 会变成两个乱码。这就是因为 GB2312 编码与 UTF8 编码产⽣了编码冲撞造成的。
在UTF-8编码⽂件中BOM在⽂件头部,占⽤三个字节,⽤来标⽰该⽂件属于UTF-8编码。UTF-8 的BOM
对UFT-8没有作⽤,是为了⽀持UTF-16,UTF-32才加上的BOM,BOM签名的⽬的是告诉编辑器当前⽂件采⽤何种编码,⽅便编辑器识别,但是BOM虽然在编辑器中不显⽰,但是会产⽣输出,有些软件不识别BOM的头,打开保存后会识别出错。
在不同的Unicode编码中,对应的BOM的⼆进制字节Bytes Encoding如下:
FE FF UTF16BE
FF FE UTF16LE
EF BB BF UTF8
因此,我们可以根据⽂件头部的⼏个字节和上⾯的表格对应来判断该⽂件是哪种编码形式。
⼆.、如何获取⽂件的BOM字符
BOM头在记事本中是看不到的,通过程序读取⽂件是可以识别的,也可以打印出来
// <Buffer ef bb bf 68 65 6c 6c 6f 20 77 6f 72 6c 64>
// 前三个字节就是对应的我们UTF8编码的⽂本的BOM头字符
参考JAVA识别代码:
/**
* 判断⽂件编码
*
* @param inputStream
* @return
*/
private String getFilecharset(InputStream inputStream) {
String charset = "GBK";
byte[] first3Bytes = new byte[3];
try {
boolean checked = false;
BufferedInputStream bis = new BufferedInputStream(inputStream);
bis.mark(0);
int read = ad(first3Bytes, 0, 3);
if (read == -1) {
return charset; // ⽂件编码为 ANSI
} else if (first3Bytes[0] == (byte) 0xFF
&& first3Bytes[1] == (byte) 0xFE) {
charset = "UTF-16LE"; // ⽂件编码为 Unicode
checked = true;
} else if (first3Bytes[0] == (byte) 0xFE
&& first3Bytes[1] == (byte) 0xFF) {
charset = "UTF-16BE"; // ⽂件编码为 Unicode big endian
checked = true;
} else if (first3Bytes[0] == (byte) 0xEF
&& first3Bytes[1] == (byte) 0xBB
&& first3Bytes[2] == (byte) 0xBF) {
charset = "UTF-8"; // ⽂件编码为 UTF-8
checked = true;
}
if (!checked) {
int loc = 0;
while ((read = ad()) != -1) {
loc++;
if (read >= 0xF0)
break;
session如何设置和读取if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK
break;
if (0xC0 <= read && read <= 0xDF) {
read = ad();
if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
// (0x80
/
/ - 0xBF),也可能在GB编码内
continue;
else
break;
} else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错,但是⼏率较⼩
read = ad();
if (0x80 <= read && read <= 0xBF) {
read = ad();
if (0x80 <= read && read <= 0xBF) {
charset = "UTF-8";
break;
} else
break;
} else
break;
}
}
}
bis.close();
} catch (Exception e) {
e.printStackTrace();
<("getFilecharset exception ",e);
}
return charset;
}
三、BOM头存在哪些问题
虽然BOM头起到了标记⽂件编码的作⽤,但是他并不属于⽂件的内容部分,类似WINDOWS⾃带的记事本等软件,在保存⼀个以UTF-8编码的⽂件时,会在⽂件开始的地⽅插⼊三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。它是⼀串隐藏的字符,⽤于让记事本等编辑器识别这个⽂件是否以UTF-8编码。对于⼀般的⽂件,这样并不会有什么问题。
但在某些场景下,会产⽣⼀些额外的问题:
1、⽐如我们把⼏个JS⽂件合并成⼀个⽂件后,如果⽂件中间含有BOM字符,就会导致浏览器JS语法错误。
2、对于 PHP来说,BOM是个⼤⿇烦。PHP并不会忽略BOM,所以在读取、包含或者引⽤这些⽂件时,会把BOM作为该⽂件开头正⽂的⼀部分。根据嵌⼊式语⾔的特点,这串字符将被直接执⾏(显⽰)
出来。由此造成即使页⾯的 top padding 设置为0,也⽆法让整个⽹页紧贴浏览器顶部,因为在html⼀开头有这3个字符呢!最⼤的⿇烦还不是这个。受COOKIE送出机制的限制,在这些⽂件开头已经有BOM的⽂件中,COOKIE⽆法送出(因为在COOKIE送出前PHP已经送出了⽂件头),所以登⼊和登出功能失效。⼀切依赖COOKIE、SESSION实现的功能全部⽆效。
四、如何去掉⽂件中的BOM头
1、在⽂件另存为的时候选择⽆BOM头的UTF8编码
2、使⽤node中的⽂件模块获取⽂件的buffer数据并去掉前三个字节,代码如下:
function deleteUtf8BomHead(path) {
let buf = fs.readFileSync(path);
if (buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf) {
buf = buf.slice(3)
}
return buf
}
3、EDITPLUS去BOM头的⽅法
编辑器调整为UTF8编码格式后,保存的⽂件前⾯会多出⼀串隐藏的字符(也即是BOM),⽤于编辑器识别这个⽂件是否是以UTF8编码。
运⾏Editplus,点击⼯具,选择⾸选项,选中⽂件,UTF-8标识选择总是删除签名,然后对PHP等⽂件编辑和保存后的PHP⽂件就是不带BOM的了。
4、UltraEdit去除BOM头办法
打开⽂件后,另存为选项的编码格式⾥选择(UTF-8 ⽆BOM头)保存。
备注:
1、在编辑、更改任何⽂本⽂件时,请使⽤不会乱加BOM的编辑器。Linux下的编辑器⽬前没发现这个问题。Windows下,请勿使⽤记事本等编辑器。推荐的编辑器是:notepad ++, Editplus 2.12版本以
上, EmEditor, UltraEdit(需要取消‘添加BOM’的相关选项),Dreamweaver(需要取消‘添加BOM’的相关选项)等。对于已经添加了BOM的⽂件,要取消的话,可以⽤以上编辑器另存⼀次。(Editplus 需要先另存为GB,再另存为UTF-8。)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论