gbk与utf8自动识别
1. 背景
在通过geotools读取shp文件到postgis数据库的时候,首先要名称当前shp文件的编码格式。如果编码格式没有搞清楚,那么导入数据库的记录就会是乱码。对于shp文件编码格式的定义,在他的文件集中有个以cpg结尾的文件,这个文件记录了shp文件的编码格式。然而,不幸的是不是所有的shp文件集包含这个文件。此时,我们就需要通过另外一种途径判断字符串的编码格式。
2. 上代码
我们先把解决方式展现出来,如果只仅仅为了解决问题,那直接拷贝走就可以,但是如果想一探究竟的话,可以继续往下看。需要注意的是,这个方法在概率上并不是完全正确,因为可能出现极端情况而影响结果的准确性。
/*** 判断字符串的编码格式* @param str 需要判断的字符串* @return 判断的编码格式,如果返回null,则不能判断编码*/
private String checkCharSet(String str) {try {int lenUtf8 = new String(str.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8).length();int lenGbk = new String(str.getBytes(StandardCharsets.ISO_8859_1), "GBK").length();//字符串不包含中文,不能判断是哪一种编码集if (lenGbk == lenUtf8) {return null;}return lenUtf8 > lenGbk ? "GBK" : "UTF-8";} catch (Exception e) {log.error(e.getMessage(), e);}return "UTF-8";
}
大体思路就是对一个字符串分别用utf-8与GBK的方式读取,如果哪个字符短,那就属于哪个编码格式。那么下面我们就论证一下这个说法。
3. UTF-8编码
3.1 UTF-8编码概述
UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。
0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
通过上图我们可以知道,0xxxxxxx
是占用一个字节,那么他最大代表0~127(000000000 ~ 011111111)。这个也就是ASCII编码区,即utf-8完全兼容ASCII编码。我们知道,一个汉子的3个字节,所以对应1110xxxx 10xxxxxx 10xxxxxx
。
3.2 UTF-8编码规则
通过上面分析,我们知道汉子的编码为3个字节。有效字节为画x的部分,也就是两个字节(4 + 6 + 6)的长度。
3.3 UTF-8编码举例
3.3.1 三个字节举例
我们以“河”为例,进行说明。
确认Unicode编码
通过查询Unicode对照表,可以知道河的Unicode码为:6cb3,网上转换工具一大片,大家可以自行查找。
转换为二进制数
6c b3 0110 1100 1011 0011
转换为utf-8编码
我们知道,对于一个三个字节的utf-8码(1110 xxxx 10xx xxxx 10xx xxxx),第一个字节剩余四个bit,第二个和第三个字节剩余六个bit,所以最终转换的utf-8码为:
1110 0110 10110010 10110011
验证
我们通过java代码工具做一个验证。
@Test public void utf8test(){String msg = "河";byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);System.out.println(Arrays.toString(bytes)); } //[-26, -78, -77] // -26:1110 0110 // -78:1011 0010 // -77:1011 0011
3.3.2 两个字节举例
我们以“h”为例,进行说明。h
虽然属于ascii码定义范畴,但是他在Unicode也定义了相应的编码。这个案例我们采用反向的方式进行,也就是解码。
查找h对应的Unicode码,04BB。
将04bb转换为二进制为:
100 1011 1011
转换为utf-8的编码:
11010010 10101011
将两个二进制字节转换为有符号整数,为[-46,-69]
执行程序,验证输出
@Test public void utf8(){byte[] bytes = {-46,-69};String s = new String(bytes, StandardCharsets.UTF_8);System.out.println(s); }
3.3.3 非法UTF-8举例
utf-8具有严格的编码规范,如果不按照这个规则来编码回事什么情况,我们就做一个实验。
我们就以11101001 10101001
举例。我们知道,以1110开头的utf-8需要三个字节。而此时提供了两个字节,看看他输出什么结果?
@Test
public void validUtf8(){byte b1 = (byte)Integer.parseInt("11101001", 2);byte b2 = (byte)Integer.parseInt("10101001", 2);byte[] bytes = {b1,b2};String s = new String(bytes, StandardCharsets.UTF_8);System.out.println(s);System.out.println(Arrays.toString(s.getBytes()));
}
//�
//[-17, -65, -67]
在输出结果中,我们发下出现一个乱码一个数组[-17, -65, -67],这个数组就是针对不合法的UTF-8的标识。
需要注意的是,这里只有一个字符。抓换为二进制为:11101111 10111111 10111101
我们再以11101001 11101001
举例:
@Test
public void validUtf8(){byte b1 = (byte)Integer.parseInt("11101001", 2);byte b2 = (byte)Integer.parseInt("11101001", 2);byte[] bytes = {b1,b2};String s = new String(bytes, StandardCharsets.UTF_8);System.out.println(s);System.out.println(Arrays.toString(s.getBytes()));
}
//��
//[-17, -65, -67, -17, -65, -67]
@Test
public void validUtf8(){byte b1 = (byte)Integer.parseInt("10101001", 2);byte b2 = (byte)Integer.parseInt("11101001", 2);byte[] bytes = {b1,b2};String s = new String(bytes, StandardCharsets.UTF_8);System.out.println(s);System.out.println(Arrays.toString(s.getBytes()));
}
//��
//[-17, -65, -67, -17, -65, -67]
结论:
- 如果以110或者1110等开头的话,他会吸收后面以10开头的字节,作为一个错误编码
- 如果以10开头,则按照一个错误编码。
4. GBK编码
GBK
编码,是对GB2312
编码的扩展,因此完全兼容GB2312-80
标准。GBK
编码依然采用双字节编码方案,其编码范围:8140-FEFE
,剔除xx7F
码位,共23940个码位。共收录汉字和图形符号21886个,其中汉字(包括部首和构件)21003个,图形符号883个。GBK
编码支持国际标准ISO/IEC10646-1
和国家标准GB13000-1
中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK
编码方案于1995年12月15日正式发布,这一版的GBK
规范为1.0版。
4.1 GBK编码规则
当使用GB2312编码标准时,给定一串字符编码,按照字节进行检测,首先检测每个字节的大小,如果字节值小于0x7F(字节首位为0)
,就用ASCII标准解码,如果遇到一个大于0x7F(字节首位为1)
的字节,就把该字节和它后面一个字节连在一起用GBK标准进行解码,然后从第三个字节开始继续遍历检测。
4.2 举例
我们还以"河"为例,说明他的编码方式:
- 首先查找"河"所对应的GB2312编码,注意不是unicode编码,BAD3,参考地址[GB2312](在线GBK编码表-GBK中文字符集-在线GBK汉字编码字符集对照表 (jsons.cn))
- 转换为二进制位:
1011 1010 1101 0011
- 转换为十进制数字:[-70, -45]
5. 检测说明
我们在回到刚开始的问题,主要是按照另外一种情况解码,那么长度必定大于等于当前字符串长度。
5.1 GBK以UTF-8解码
我们知道GBK编码后是其长度的二倍。按照正常情况下,utf-8的长度应该是len(gbk) * 2 / 3,这样看来长度比GBK短。但是前提合法。UTF-8有非常严格的编码规则,很多情况下,就会出现每个字符生成一个错误字符,这样看来长度应该是GBK的二倍。
5.2 UTF-8以GBK解码
这个很好理解,UTF-8字节长度是其3倍,所以如果正常情况下,GBK的长度应该是UTF8 * 3 / 2,正常情况下都比utf8长。
当然,如果全部是英文,上述方法是不能判断编码格式的。
gbk与utf8自动识别相关推荐
- GBK和UTF8什么区别
GBK和UTF8什么区别 http://wshw1982.blog.163.com/blog/static/149293620126114239414/ GBK的文字编码是双字节来表示的,即不论中.英 ...
- gbk与utf-8的区别
很多用户问GBK与UTF-8版本有什么区别,这边作了个整理如下: ***CMS的GBK版本与UTF-8版本功能是一样的.只不过编码方式不同. GBK的文字编码是双字节来表示的,即不论中.英文字符均使用 ...
- GB2312、GBK与UTF-8的区别
GB2312.GBK与UTF-8的区别 这是一个异常经典的问题,有无数的新手站长每天都在百度这个问题,而我,作为一个"伪老手"站长,在明白这个这个问题的基础上,有必要详细的解答一下 ...
- 项目乱码 GBK转UTF-8工具
项目乱码 GBK转UTF-8工具 链接:http://pan.baidu.com/s/1pLw1mMB 密码:rj6c 转载于:https://www.cnblogs.com/imsoft/p/532 ...
- python3编码和解码_python3的url编码和解码,自定义gbk、utf-8的例子
因为很多时候要涉及到url的编码和解码工作,所以自己制作了一个类,废话不多说 码上见! # coding:utf-8 import urllib.parse class Urlchuli(): &qu ...
- 编码与乱码(05)---GBK与UTF-8之间的转换--转载
原文地址:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html [GBK转UTF-8] 在很多论坛.网上经常有网友问&q ...
- 再谈java乱码:GBK和UTF-8互转尾部乱码问题分析
一直以为java中任意unicode字符串可以使用任意字符集转为byte[]再转回来只要不抛出异常就不会丢失数据事实证明这是错的. 经过这个实例也明白了为什么 getBytes()需要捕获异常虽然有时 ...
- C#_汉字与GBK,Unicode,UTF-8编码之间的转换
IT发展至今,字符编码版本众多,目前流行的GBK,Unicode,UTF-8编码与汉字的转换可用如下代码: private void button1_Click(object sender, Even ...
- python3默认编码_python3的url编码和解码,自定义gbk、utf-8的例子
因为很多时候要涉及到url的编码和解码工作,所以自己制作了一个类,废话不多说 码上见! # coding:utf-8 import urllib.parse class Urlchuli(): &qu ...
最新文章
- c语言溢出该怎么算,解决整数运算溢出方法——C语言
- ITK:二进制图像的近似距离图
- 洛谷P1709 [USACO5.5]隐藏口令Hidden Password
- 2016 亚洲共识指南:肺结节的评估
- 析构函数和动态内存管理
- mysql用代码建表基础语法
- 控件安全注册方法3选
- 用户画像有什么用?怎样用?6个场景案例给你讲明白
- Linux(ARM glibc)使用libhybris调用Android(ARM bionic)
- 大数据——python爬虫
- 【系统安全学习5】PGP加密
- python autoit上传文件_Python selenium使用autoIT上传附件过程详解
- 从键盘上输入以下的数据:TOM:89|JERRY:90|TONY:95,数据格式为“姓名:成绩|姓名:成绩|姓名:成绩”,对输入的内容按成绩进行排序,并将结果按成绩由高到低排序。
- 会员动向丨威蓝汽车、深信科创、智行众维等成为ASAM会员
- 超动感,百行Python代码制作动态气泡图
- Leetcode-数组-904
- Xilinx ZYNQ开发板资料共享
- MySQL及Navicat的安装和使用
- 如何使用OpenCV在图像中抠出指定的颜色区域
- wireshark抓图
热门文章
- 你的Scrum迭代够精益吗?看完就全明白了
- LimeSDR实验教程(10) DVB-S发射和接收
- Excel批量去除绿三角的2种技巧,方法1可以一劳永逸【附视频操作】
- (原创)分词的被动语态中现在式和过去式的区别
- 奇瑞新能源销量回升,iCar或延续辉煌
- 2021年化工自动化控制仪表考试试题及化工自动化控制仪表操作证考试
- AgileEAS.NET5.0-界面设计器-使用说明书(上)
- 搜狗输入法突然变成繁体怎么解决?
- 基于参考图像的人脸组成编辑
- 程序员用Python分析徐峥,竟研究出《我不是药神》30亿票房真正秘诀!