SAS岩论|在SAS中如何解决中文乱码问题(2)-干货篇
首先,GBK编码方案中,总体的编码是有范围的,从8140~FEFE。其中首字节在81~FE之间,尾字节在40~FE之间,剔除xx7F一条线;总计23940个码位,共收入21886个汉字和图形符号,其中汉字21003个,图形符号883个。...
上一期文章对中文乱码产生的情况进行了原因剖析,也提供了从底层解决问题的思路。本篇文章就上期文章的思路进行代码实现。上期文章传送门:
SAS岩论|在SAS中如何解决中文乱码问题(1)
整体解决思路
首先,GBK编码方案中,总体的编码是有范围的,从8140~FEFE。其中首字节在81~FE之间,尾字节在40~FE之间,剔除xx7F一条线;总计23940个码位,共收入21886个汉字和图形符号,其中汉字21003个,图形符号883个。其次,ASCII编码的范围是从00~7F,共计128个码位,收入大小写英文字母、数字、符号、及特殊控制字符。其中可显示的编码范围为20~7E;
因此整体的解决过程为:
- 将字符串转化为16进制编码;
- 首先提取前2位编码,判断这两位编码是否大于等于81,进而决定是否是中文;
- 如果小于81则认为是英文字符,直接将其保存;如果大于等于81,则是中文的开始,进行标记,保存到临时的变量,等待第二个字节信息;
- 继续下次循环提取第二个字节编码(每个字节2位编码),通过标记判断当前编码是否属于中文第二个字节;如果不是,继续第三步的判断;如果是,则判断该字节编码是否大于40,且不等于7F。如果满足则说明中文没问题,则追加至临时变量,然后直接输出;如果不满足,则说明上个字节是中文断码,忽略上个字节信息;然后继续第3步判断,继续流程,直至全部编码处理完毕。
没有必要而有意义的过程
要对十六进制编码进行大小的比较,首先需要将其转化为十进制。于是我就直接敲击键盘写出了十六进制转十进制的macro。但是后来,才想来直接用input函数就可完成。但是,这个不必要的过程我觉得还是很有意义分享一下。主要有两点:- SAS Macro的强大及方便;
- 在Data步中实时调用执行宏;
示例数据准备
为了演示解决代码,首先利用SAS代码来人为制造一份数据:/*
“我是1个SAS用户”,其GBK编码为:CED2CAC731B8F6534153D3C3BBA7
其中,我:CED2;是:CAC7;1:31;个:B8F6;S:53;A:41;S:53;用:D3C3;户:BBA7
基于此生成4条样例数据:
- 第一条为完整的;
- 第二条去掉“户”的最后两个编码;
- 第三条去掉“是”的最后两个编码;
- 第四条去掉“我”的最后两个编码;
data testdata;
length code $ 30 str $ 15;
input code $;
str=input(code,$hex30.);
cards;
CED2CAC731B8F6534153D3C3BBA7
CED2CAC731B8F6534153D3C3BB
CED2CA31B8F6534153D3C3BBA7
CECAC731B8F6534153D3C3BBA7
;
run;
首先我们对编码进行人工的判断:
- 第一条数据没有问题,就是:“我是1个SAS用户”;
- 第二条数据末尾,存在BB这个断码(乱码),因此无法显示,需要处理。处理的方法就是删除这个断码BB,从而结果变成“我是1个SAS用”;
- 第三条数据,原本“是1”对应编码CAC731变成CA31;由于CA31没有对应的汉字(汉字的第二字节编码范围是40~FE),因此此处会形成一个乱码,无法显示,需要处理。处理的方法就是删除CA这个断码删除,31继续显示为数字“1”,因此处理后结果为“我1个SAS用户”;
- 第四条数据,原本“我是1”对应编码CED2CAC731,变成CECAC731;碰巧CECA对应汉字“问”,而C731跟上面一样是一个乱码,因此也无法显示,需要处理。由于CECA碰巧形成一个新的汉字,不会处理,只会将C7这个断码删除,31继续显示为数字“1”,因此处理后结果为“问1个SAS用户”;
上干货
为了排版效果,只能截图了:结果如下所示:
好了,关于中文乱码的处理程序到此就结束了,如果各位有更好的意见,欢迎评论留言。(想要代码请在SAS中文论坛微信群获取)
补上之前的宏程序
废话不多说,直接上代码:
%macro HexTranscode(h);
%if%eval(&h ge 0 and &h le 9) %then &h;
%else%if%upcase(&h)=A %then10;
%else%if%upcase(&h)=B %then11;
%else%if%upcase(&h)=C %then12;
%else%if%upcase(&h)=D %then13;
%else%if%upcase(&h)=E %then14;
%else%if%upcase(&h)=F %then15;
%mend;
%macro HexToDecimal(hex);
%let length=%length(&hex);
%do i=1%to &length;
%let h&i=%HexTranscode(%substr(&hex,&i,1));
%end;
%let result=0;
%do i=1%to &length;
%let result=%eval(&result +&&h&i*(16**%eval(&length-&i)));
%end;
&result
%mend;
%put %HexToDecimal(81);
%put %HexToDecimal(8140);
日志结果:
怎么样?用sas macro完成十六进制到十进制的转换是不是很完美?还不会macro的SAS小白,速度学起!我相信用其他语言来完成这个转换过程估计没这么完美吧(不能用内置的函数转换)?如果有欢迎留言区上代码!我定会好好学习!
另外一个点是如何在Data步中实时的获取宏的结果。学习过macro的小伙伴都知道,如果直接在data步中含有直接的macro调用,macro是先于data步解析执行的。所以等data步开始执行的时候,macro的结果已经定了。
所以如何在Data步中来实时解析调用该宏来得到转换结果,各位小伙伴可以想一想,下期分享再说。
1月原创好文回顾,请点击阅读:
SAS岩论|在SAS中如何解决中文乱码问题(1)
SAS岩论|SAS EG中如何显示Data步处理进度
关注 SAS中文论坛
微信扫一扫关注公众号