Caché 字符编码自动判断

先说几个场景:

  1. 使用文件字符流打开一个文本文档,但是我不确定是以UTF8编码的还是GB18030,所以就无法准确设置TranslateTable,就导致了中文乱码问题。
  2. 有一个文件下载的csp,其中文件名参数可能是中文,如果在一个UTF8编码的界面直接调用时,后台取到的文件名就会是乱码。
  3. 接收到字节流后需要转成字符流读取内容,但是无法确定编码格式,就无法准确的转成字符。

以上几个场景虽然大多都可以提前做好约定解决,但是可能有历史原因或者种种情况,需要我们自己能够解决,于是就有了下面的故事。

基础

首先我方系统使用GB18030编码,然后碰到的情况大多都是对方可能是UTF8编码,所以主要来解决识别字节流是不是UTF8编码的。

然后查了一个UTF8编码格式

  • 1字节 0xxxxxxx
  • 2字节 110xxxxx 10xxxxxx
  • 3字节 1110xxxx 10xxxxxx 10xxxxxx
  • 4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  • 5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  • 6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

有了UTF8编码格式,然后逐字节进行判断,看整个字节序列是否完美符合UTF8编码,所以先实现了方法IfMatchUTF8Bytes

/// 是否是符合UTF8的字节序列
/// bytes 字节串或字节流
/// maxLen 最大验证长度 超过此长度的不再验证
/// Output Count  符合1-6字节编码标准的字符数 $lb(f1,f2,f3,f4,f5,f6)
/// 返回值 1符合 0不符合
ClassMethod IfMatchUTF8Bytes(bytes, maxLen = 30000, Output Count)
{
    ///code
}

基于IfMatchUTF8Bytes方法,然后又实现了一个方法GuessUTF8Bytes,此方法又做了一些其它的判断:如果前三字节为UTF8BOM则直接认定为UTF8,如果校验结果中只有1、2字节的字符,而3-6字节字符没有,则认定为不是UTF8编码。

/// 猜测是否是UTF8字节序列
/// bytes 字节串或字节流
/// 返回值 1符合 0不符合
ClassMethod GuessUTF8Bytes(bytes)
{
    ///code
}

场景1

基于以上,我们就可以实现一个方法打开某文件获得字符流,自动判断编码并设置TranslateTable为相应编码了。

/// 打开某文件获得字符流(自动判断编码并设置TranslateTable为相应编码 目前只支持UTF8)
/// s fileSteam=##class(BSP.SYS.COM.Charset).OpenFileCharacterStream(fullName)
ClassMethod OpenFileCharacterStream(fullName = "", autocharset) As %FileCharacterStream
{
	s fileSteam=##class(%FileCharacterStream).%New()
	
	s sc=fileSteam.LinkToFile(fullName)
	if $$$ISERR(sc) {
		q ""
	}
	s oldTranslateTable=fileSteam.TranslateTable
	s fileSteam.TranslateTable="RAW"  //判断字节 需要先将TranslateTable设置成RAW
	if ..GuessUTF8Bytes(fileSteam) {
		d fileSteam.Rewind()
		s fileSteam.TranslateTable="UTF8"
	}else{
		d fileSteam.Rewind()
		s fileSteam.TranslateTable=oldTranslateTable	
	}
	q fileSteam
}

这样我们就可以随便读取本地的文本文件了,也不会乱码了,解决场景1。

在这里插入图片描述

场景2

如何判断一个请求它的参数是以UTF8编码的还是以GB18030呢,通过测试发现我们可以通过GetCgiEnv("QUERY_STRING")获取到请求的参数的,且测试发现不同浏览器设置可能不同,有的为URL编码后的,有的则只是按UTF8或GB18030编码后的字节序列。对于URL编码后的可以先使用$zconvert(url,"I","URL")解码获得字节序列再进行判断,故而实现了方法IfMatchUTF8EscapedURLGuessUTF8EscapedURL

/// 是否是符合UTF8编码并进行URL编码的字符串
/// url 字符传
/// maxLen 最大验证长度 超过此长度的不再验证
/// Output Count  符合1-6字节编码标准的字符数 $lb(f1,f2,f3,f4,f5,f6)
/// 返回值 1符合 0不符合
ClassMethod IfMatchUTF8EscapedURL(url, maxLen = 30000, Output Count)
{
	s bytes=$zconvert(url,"I","URL")
	s ret=..IfMatchUTF8Bytes(bytes,maxLen,.Count)
	q ret
}
/// 猜测是符合UTF8编码并进行URL编码的字符串
/// url 字符传
/// 返回值 1符合 0符合
ClassMethod GuessUTF8EscapedURL(url)
{
    ///code
}

基于以上就可以实现一个方法判断请求调用方是不是UTF8编码的了。

/// req %CSP.Request对象
/// 返回值 1符合 0不符合
ClassMethod GuessUTF8Request(req As %CSP.Request)
{
    ///code
}

另外由于编码格式不一致,通过%requet.Data拿到的数据就会出现乱码,此时可以通过将自己解析QUERY_STRING来获取数据,于是有了ParseRequestData

/// 解析请求数据 (自动判断编码 ,目前只支持UTF8)
/// 目前只实现了从QUERY_STRING解析 在请求体的暂未找到原始数据
/// req %CSP.Request对象
/// data ByRef 解析出来的数据 
ClassMethod ParseRequestData(req As %CSP.Request, ByRef data)
{
    ///code
}

似乎这样就解决了场景二
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

场景3

有了上面的基础,似乎场景3自然而然水到渠成了。字节流转字符流首先想到的是读取字节,然后使用$zcvt(bytes,"I",charset)转换,但是这种需要注意不要将一个字符的多个字节分成了两段。此处是利用了%FileCharacterStream做了一次中转,使用其TranslateTable进行转换,不知是否有其它方法支持

/// 将字节串转换为字符串(自动猜编码,目前只支持UTF8)
/// 将字节流转换为字符流(自动猜编码,目前只支持UTF8)
/// bytes 字节串或字节流
/// 返回值 字符串或字符流
/// w ##class(BSP.SYS.COM.Charset).Bytes2Chars($zcvt("测试","O","GB18030") )
ClassMethod Bytes2Chars(bytes)
{
    ///code
}

在这里插入图片描述

代码下载

Cache字符编码自动判断.zip

总结

一个小玩具,分享出来大家看看

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值