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自动识别相关推荐

  1. GBK和UTF8什么区别

    GBK和UTF8什么区别 http://wshw1982.blog.163.com/blog/static/149293620126114239414/ GBK的文字编码是双字节来表示的,即不论中.英 ...

  2. gbk与utf-8的区别

    很多用户问GBK与UTF-8版本有什么区别,这边作了个整理如下: ***CMS的GBK版本与UTF-8版本功能是一样的.只不过编码方式不同. GBK的文字编码是双字节来表示的,即不论中.英文字符均使用 ...

  3. GB2312、GBK与UTF-8的区别

    GB2312.GBK与UTF-8的区别 这是一个异常经典的问题,有无数的新手站长每天都在百度这个问题,而我,作为一个"伪老手"站长,在明白这个这个问题的基础上,有必要详细的解答一下 ...

  4. 项目乱码 GBK转UTF-8工具

    项目乱码 GBK转UTF-8工具 链接:http://pan.baidu.com/s/1pLw1mMB 密码:rj6c 转载于:https://www.cnblogs.com/imsoft/p/532 ...

  5. python3编码和解码_python3的url编码和解码,自定义gbk、utf-8的例子

    因为很多时候要涉及到url的编码和解码工作,所以自己制作了一个类,废话不多说 码上见! # coding:utf-8 import urllib.parse class Urlchuli(): &qu ...

  6. 编码与乱码(05)---GBK与UTF-8之间的转换--转载

    原文地址:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html [GBK转UTF-8] 在很多论坛.网上经常有网友问&q ...

  7. 再谈java乱码:GBK和UTF-8互转尾部乱码问题分析

    一直以为java中任意unicode字符串可以使用任意字符集转为byte[]再转回来只要不抛出异常就不会丢失数据事实证明这是错的. 经过这个实例也明白了为什么 getBytes()需要捕获异常虽然有时 ...

  8. C#_汉字与GBK,Unicode,UTF-8编码之间的转换

    IT发展至今,字符编码版本众多,目前流行的GBK,Unicode,UTF-8编码与汉字的转换可用如下代码: private void button1_Click(object sender, Even ...

  9. python3默认编码_python3的url编码和解码,自定义gbk、utf-8的例子

    因为很多时候要涉及到url的编码和解码工作,所以自己制作了一个类,废话不多说 码上见! # coding:utf-8 import urllib.parse class Urlchuli(): &qu ...

最新文章

  1. c语言溢出该怎么算,解决整数运算溢出方法——C语言
  2. ITK:二进制图像的近似距离图
  3. 洛谷P1709 [USACO5.5]隐藏口令Hidden Password
  4. 2016 亚洲共识指南:肺结节的评估
  5. 析构函数和动态内存管理
  6. mysql用代码建表基础语法
  7. 控件安全注册方法3选
  8. 用户画像有什么用?怎样用?6个场景案例给你讲明白
  9. Linux(ARM glibc)使用libhybris调用Android(ARM bionic)
  10. 大数据——python爬虫
  11. 【系统安全学习5】PGP加密
  12. python autoit上传文件_Python selenium使用autoIT上传附件过程详解
  13. 从键盘上输入以下的数据:TOM:89|JERRY:90|TONY:95,数据格式为“姓名:成绩|姓名:成绩|姓名:成绩”,对输入的内容按成绩进行排序,并将结果按成绩由高到低排序。
  14. 会员动向丨威蓝汽车、深信科创、智行众维等成为ASAM会员
  15. 超动感,百行Python代码制作动态气泡图
  16. Leetcode-数组-904
  17. Xilinx ZYNQ开发板资料共享
  18. MySQL及Navicat的安装和使用
  19. 如何使用OpenCV在图像中抠出指定的颜色区域
  20. wireshark抓图

热门文章

  1. 你的Scrum迭代够精益吗?看完就全明白了
  2. LimeSDR实验教程(10) DVB-S发射和接收
  3. Excel批量去除绿三角的2种技巧,方法1可以一劳永逸【附视频操作】
  4. (原创)分词的被动语态中现在式和过去式的区别
  5. 奇瑞新能源销量回升,iCar或延续辉煌
  6. 2021年化工自动化控制仪表考试试题及化工自动化控制仪表操作证考试
  7. AgileEAS.NET5.0-界面设计器-使用说明书(上)
  8. 搜狗输入法突然变成繁体怎么解决?
  9. 基于参考图像的人脸组成编辑
  10. 程序员用Python分析徐峥,竟研究出《我不是药神》30亿票房真正秘诀!