`
sd6733531
  • 浏览: 66521 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论
阅读更多

 

对于字符处理有两个印象比较深刻的事儿。第一个就是在学校里面写个小应用,用Java读文件结果乱码了。第二个就是用ant做打包,结果在编译class的过程中老是报语法错误。结果原来是用记事本编辑了下,加了BOM所致。

一直认为从纸带时代到达字符时代是个伟大的跨越。可是如今纷繁的字符编码却让人觉得眼花缭乱,不知所从。最简单的做法便是一切UTF-8,标准。

前段时间公司内部正好搞字符培训,留了几个题目。刚好手头活儿不多,便利用闲暇时间做了下。边做边查wiki,总算对字符编码有了比较深刻的认识了。

首先了解了编码名字的意思。GB表示国家标准,CJK表示China+Japan+Korea(中日韩),UTF-8Unicode Transform)BIG5表示5个参与编码的IT巨头。

Unicode是标准的字符编码,Unicode收录了世界上几乎所有的字符,然后将每个字符与二进制值相对应。(http://zh.wikibooks.org/wiki/Unicode/6000-6FFF)


 

上图便展示了Unicode字符编码。问题是,有Unicode不是已经足够了?为什么还要GBK,UTF-8BIG5呢?

我觉得有几个因素。首先,在计算机由卡带向字符过渡的时候。收录世界上所有的字符,并将其编码比较困难。当时互联网也没有兴起,人们并没有意识到需要跨文化交流。所以那时基本上是每个国家把自己的所有字符整理出来,先自行编码。

当互联网兴起,人们有跨文化交流欲望的时候。Unicode联盟便站出来,将那些分散全世界的字符编码加以收录整合,才做出来标准的Unicode

Unicode编码工作完成只是解决了收录全世界字符并且给每个字符一个唯一id的任务。对Unicode进一步编码,便形成了UTF-8UTF-16UTF-32之类。

为什么要进一步编码?Unicode字符从0~e0fff。最大的字符\uE0FFF达到了2.5个字节,\u0则只需要半个字节。同样是一个字符,占用的字节数有多有少。如果碰到0x2d 0x3e 0xf7 0x44这一串byte,你该怎么解释。我可以解释成为0x2d,0x3e 0xf7,0x44,也可以解释成0x2d 0x3e,0xfe 0x44。两种解释,所取得的字符就不一样了。

也许你会说,既然目前Unicode最多只到2.5个字节。那么我学ipv6,冗余一下,预留几个扩展,每个字符编码4个字节不就得了。且慢,这样做并不好。首先,字符是大量出现的,冗余会造成很大的空间浪费。原先用UTF-8编码只需要2MB的文件,照这样一编码恐怕就需要4MB了。其次,ipv4-ipv6可以通过网络拓扑升级实现无痛升级,可如果你原来是4个字节一个字符,忽然来了外星文化,字符多了一倍,你发现不够使,想改到8个字节一个字符。你咋升级?以前的字符怎么兼容啊。最后,这样做还不利于定位。如果我想从第30个字节开始截取30个字符的文本。如果截取的不是头位置,那么后面所有的文本都会乱掉。

下面来看几个比较有意思的例子。


 

 

GBK编码的第7个字节是中的低字节位。如果从这里截断的话,会发生什么呢?

 


  

 

发现乱码了。其原因便是上面所述的断句错误产生的。

如果是UTF-8编码,从非法位置截断的结果又是这样子的。(因为只有收费版的winhex才可以自由选择编码格式,这里只能写个程序验证下了)

 

String string="2013年中国经济的增长目标是目前市场舆论的焦点";
byte[] utf8C=string.getBytes("utf8");
dumpCode(utf8C);
System.out.println(new String(Arrays.copyOfRange(utf8C, 6, utf8C.length),"utf8"));

 

 

 

6这个位置是年的中间位(汉字是3个字节,ascii1个字节)。看看截断后的结果吧

 

��国经济的增长目标是目前市场舆论的焦点

 

 

咦,虽然截断了。可是没有像GBK那样全部乱掉,而只是非法位置的那个字符才乱掉了。由此看来似乎UTF-8GBK还先进一点。

我们来参照一下GBK的编码规则(http://zh.wikipedia.org/wiki/GBK)。GBK对ASCII码做了兼容,1个字节的都是ASCII码,2个字节的是通用汉字字符。(所谓兼容,就是用GBK编码规则展示的ASCII文本,与ASCII编码展示的文本一模一样)。

注意2个字节的编码规则。第一位81-FE,第二位40-7E。结合上述的例子,D0开始的截断后,原本D6 D0,B9 FA(中国)变成了D0 B9,FA BE。事实上如果中间插入了明显的标志,GBK还是可以做一些修正的。例如观察到汉字字符无论高低位都不得小于40,那么如果这样子。

 

String string="2013年中国3经济的增长目标是目前市场舆论的焦点";
byte[] gbkC=string.getBytes("gbk");
dumpCode(gbkC);
System.out.println(new String(Arrays.copyOfRange(gbkC, 7, gbkC.length),"gbk"));

 

 

 

结果是:

 

 

泄�经济的增长目标是目前市场舆论的焦点

  

7的位置还是中的低字节位,但是注意“中国”后面加了个3。当识别0x33的时候,它不可能呆在汉字字符。此时GBK认识到了有问题,便做了修复。也就是说,我们错怪GBK了。它相比UTF-8来说并不是没有对错误位置的修复能力,只是它的能力比较弱而已!

看一下UTF-8的编码规则(http://zh.wikipedia.org/wiki/UTF-8#.E7.B7.A8.E5.AF.AB.E4.B8.8D.E8.89.AF.E7.9A.84.E8.A7.A3.E6.9E.90.E5.99.A8)UTF-8的字符范围从1个字节到4个字节。但是它的编码方式非常的规律整齐,高字节用开头1的数量标志后面低字节数目,低字节都是10开头。因此上面的截断程序中UTF-8非常快速的发现并修正了问题。

培训最后一道题目是用正则表达式识别文本属于什么编码格式。根据编码范围可以比较容易的写出来了。下面用强大的RegexBuddy来尝试一下

 

 

 

 

 

 

咦?怎么一个汉字也没有匹配到呢?\x匹配的是一个ASCII字符,本身只占用一个字节。而如果用GBK编码解析的话,"2013年中国"。本身10个字节被分割成了7个字符。怎么办?RegexBuddy还有强大的Hex模式,ctrl+H便可以进入。

 


 

hex模式下,果然完全匹配成功了。写个小例子来探测字符编码。

 

 

String gbkString=new String("2013年中国经济的增长目标是目前市场舆论的焦点");
String encodedGBK = new String(gbkString.getBytes("gbk"),"iso-8859-1");
dumpCode(encodedGBK.getBytes("iso-8859-1"));
		
String utf8String=new String("2013年中国经济的增长目标是目前市场舆论的焦点");
String encodedUTF8=new String(utf8String.getBytes("utf-8"),"iso-8859-1");
dumpCode(encodedUTF8.getBytes("iso-8859-1"));
		
System.out.println(PATTERN_GBK.matcher(encodedGBK).matches());
System.out.println(PATTERN_GBK.matcher(encodedUTF8).matches());
		
System.out.println(PATTERN_UTF8.matcher(encodedGBK).matches());
System.out.println(PATTERN_UTF8.matcher(encodedUTF8).matches());

 

 

 

 

UTF8GBK的正则如下。

 

 

final static Pattern PATTERN_UTF8=Pattern.compile("(" +
			"([\\x00-\\x7f])+" +
			"|([\\xc0-\\xdf][\\x80-\\xbf]{1})+" +
			"|([\\xe0-\\xef][\\x80-\\xbf]{2})+" +
			"|([\\xf0-\\xf7][\\x80-\\xbf]{3})+" +
			"|([\\xf8-\\xfb][\\x80-\\xbf]{4})+" +
			"|([\\xfc-\\xfd][\\x80-\\xbf]{5})+" +
			")+");
final static Pattern PATTERN_GBK=Pattern.compile("(" +
			"([\\x00-\\x7f])+" +
			"|([\\xa1-\\xa9][\\xa1-\\xfe])+" +
			"|([\\xb0-\\xf7][\\xa1-\\xfe])+" +
			"|([\\x81-\\xa0][\\x40-\\x7e\\x80\\xfe])+" +
			"|([\\xaa-\\xfe][\\x40-\\x7e\\x80\\xfe])+" +
			"|([\\xa8-\\xa9][\\x40-\\x7e\\x80\\xfe])+" +
				")+");

  

为什么要ISO-8859-1呢?其实还是\x的老问题,\x映射的是ascii字符。只有将String encode成一个ascii字符串才可以使用上述的二进制正则匹配。

题目只要求匹配UTF-8GBK的编码。如果是其它呢,比如UTF-16http://zh.wikipedia.org/wiki/UTF-16?根据编码范围紧接着我便写出了正则表达式,也匹配成功了。

 

 


 

 

 

 

 

可是当尝试用表达式匹配UTF-8GBK时,发现匹配也成功了。UTF-16的取值范围过大,编码特征不易识别。那么还有办法识别吗?我上Unicode的官方网站查找了一下。

(http://www.unicode.org/faq/utf_bom.html#bom1)官方网站给出了解释,UTF-16/UTF-32均支持BOM,通过特征的BOM序列可以比较容易的判断。

其实字节流,如果样本较少时。多种编码规则都可以匹配,此时判断字符编码也比较困难。BOM是个一劳永逸的终极解决方法。在Windows上面使用Notepad编辑后,可以看到它存储文件时总会在头部分加上一串BOM标识。可要注意的是,标准的UTF-8是不需要BOM的,而Windows下为了便于识别也会给UTF-8加上BOM。这也是当初我用Notepad编辑Java后编译失败的原因。

  • 大小: 134.2 KB
  • 大小: 5.5 KB
  • 大小: 4.6 KB
  • 大小: 204.8 KB
  • 大小: 296.1 KB
  • 大小: 200.5 KB
分享到:
评论
1 楼 czd327917086 2013-10-21  
写得非常好,对编码讲的很透彻!感谢分享!

相关推荐

    matlab的字符识别

    在MATLAB中进行字符识别是一项涉及图像处理、模式识别和机器学习的技术应用。MATLAB作为一个强大的数值计算和数据可视化平台,提供了丰富的工具箱,使得字符识别成为可能,尤其是在学术研究和工程实践中。以下是对...

    基于神经网络的字符识别

    在现代信息技术领域,字符识别是一项重要的技术,尤其在自动化处理文档、图像分析、OCR(Optical Character Recognition,光学字符识别)系统中起着关键作用。本项目“基于神经网络的字符识别”着重探讨如何利用神经...

    深度学习字符识别训练说明-VM3.4.pdf

    VisionMaster 3.4.0是海康机器人推出的一款深度学习字符识别训练说明文档,该文档主要介绍了使用VisionMaster3.4版本进行深度学习字符识别的训练和测试过程。深度学习字符识别技术是一种利用深度学习算法来识别图像...

    车牌字符识别字符训练样本(字符模板)

    车牌字符识别技术是计算机视觉领域中的一个重要应用,主要用于自动识别车辆车牌上的字母和数字。这一技术在智能交通、车辆管理、安全监控等多个场景中有广泛的应用。在这个特定的压缩包文件中,提供的是一个用于训练...

    Matlab车牌识别定位、分割和字符识别+深度学习【完整代码 Matlab源码第一期】

    自己的Matlab课程设计的车牌识别系统,包含识别定位,分割和字符识别,识别方法使用的是深度学习。找到其中的main函数可以直接使用,适合零基础学习使用。 自己的Matlab课程设计的车牌识别系统,包含识别定位,分割...

    深度学习单字符识别训练说明-VM3.4.pdf

    深度学习单字符识别技术是基于深度学习算法实现对单个字符的识别,它突破了传统光学字符识别(OCR)技术的局限,尤其在处理复杂背景、字符畸变和粘连等问题上有显著优势。VisionMaster 3.4(VM3.4)是一款深度学习...

    相机字符识别2_模式识别_相机字符识别_

    相机字符识别技术是模式识别和计算机视觉领域的一个重要分支,主要应用于自动车牌识别(ANPR)、工业自动化检测、文档扫描和数字化以及各种智能设备的文字读取等场景。在这个专题中,我们将深入探讨相机字符识别的...

    基于matlab的英文字符识别

    【基于MATLAB的英文字符识别】技术是一种利用计算机视觉和图像处理的方法,旨在从数字化的图像中自动识别出英文字符。这项技术的核心是光学字符识别(Optical Character Recognition,简称OCR),它在文档扫描、自动...

    Halcon联合C#的OCR字符识别系统

    《Halcon联合C#的OCR字符识别系统详解》 在当今信息化时代,光学字符识别(OCR)技术在各个领域中发挥着重要的作用,特别是在文档处理、自动化检测和图像处理方面。本文将深入探讨一个基于Halcon与C#的OCR字符识别...

    基于机器学习的轮胎字符识别python实现源码+项目使用说明.zip

    基于机器学习的轮胎字符识别python实现源码+项目使用说明.zip 测试环境: CPU: Intel Core i7-12700KF GPU: NVIDIA RTX 3080TI RAM: 32GB 注:Data文件并未上传。 一、OCR简述 1.1 OCR是什么 OCR(Optical ...

    基于特征匹配的英文印刷字符识别(1)(1).zip

    在本文中,我们将深入探讨基于特征匹配的英文印刷字符识别技术。这项技术广泛应用于自动光学字符识别(OCR,Optical Character Recognition)系统中,尤其是在处理大量英文文本数据时,能够大大提高工作效率。MATLAB...

    我的字符识别模式识别完整毕业设计

    《我的字符识别模式识别完整毕业设计》 毕业设计是学术生涯中的一个重要环节,它要求学生综合运用所学知识,解决实际问题。在这个“我的字符识别模式识别完整毕业设计”中,我们将深入探讨字符识别技术,特别是模式...

    基于matlab的模板匹配法车牌定位切割和字符识别仿真,带GUI界面+操作视频

    1.领域:matlab,基于matlab的模板匹配法车牌定位切割和字符识别算法 2.内容:基于matlab的模板匹配法车牌定位切割和字符识别仿真,带GUI界面+操作视频 3.用处:用于基于matlab的模板匹配法车牌定位切割和字符识别...

    基于STM32的数字字符识别

    在本文中,我们将深入探讨如何使用STM32微控制器实现数字字符识别技术。STM32是一种广泛应用的微控制器,由意法半导体(STMicroelectronics)生产,它具有高性能、低功耗的特点,广泛用于嵌入式系统设计。数字字符...

    基于模板匹配和谷歌开源TESSERACT库的字符识别,包含数字,字母和汉字的识别

    最近在做一个关于复杂场景下车牌识别的项目,对字符识别进行了研究。目前OCR的发展主要是基于模板匹配,TESSERACT库以及现在流行的深度学习方法进行识别。压缩包里有两个工程文件,一个是基于模板匹配的字符识别...

    国产字符识别技术,学习字符,识别源码

    在当前的数字化时代,字符识别技术扮演着至关重要的角色,特别是在文档自动化处理、图像分析、机器翻译等领域。这里我们关注的是“国产字符识别技术”,它主要用于识别和理解图像中的文字内容,尤其对于汉字的识别,...

    强大的字符识别代码(可识别图片的英文和数字)

    在IT领域,字符识别是一项关键技术,它涉及到计算机视觉和自然语言处理。本项目提供的“强大的字符识别代码”专为识别图片中的英文和数字而设计,能够将图像中的文本信息提取出来,转化为可编辑的文本格式。这个功能...

    matlab图像的简单字符识别

    在图像处理领域,字符识别是一项基础且重要的任务,特别是在OCR(Optical Character Recognition,光学字符识别)技术中。本教程将聚焦于使用MATLAB进行简单的字符识别,特别是针对图像中的字符"a"。MATLAB作为一款...

    字符识别的特征提取方法

    ### 字符识别的特征提取方法 #### 概述 字符识别技术在当今社会的应用非常广泛,包括但不限于手写体识别、印刷体字符识别、数字识别以及车牌识别等场景。为了提高字符识别系统的准确率和效率,特征提取成为了一个...

    基于Matlab的字符识别

    在IT领域,字符识别是一项重要的技术,特别是在计算机视觉和机器学习的应用中。本文将深入探讨基于Matlab的字符识别技术,以及如何通过连通域分析实现图像中的字母识别。 首先,我们要理解“字符识别”这一概念。...

Global site tag (gtag.js) - Google Analytics