GBK编码中防止串尾乱码的问题

114 篇文章 1 订阅

2012-07-14 wcdj

问题描述:

在做字符串处理时,若缓冲区的长度固定,在向缓冲区copy字符串时会出现被截断的情况。此处考虑被copy的字符串使用GBK编码并含有汉字,若出现截断则缓冲区的内容结尾可能会出现乱码。此问题会引发向DB导入数据发生异常等其他一些问题。

解决方法:

首先要了解出现这个问题的原因,即字符集编码的问题。下面是关于GBK和UTF-8的介绍:

GBK http://baike.baidu.com/view/25421.htm
UTF-8 http://baike.baidu.com/view/25412.htm
Unicode http://baike.baidu.com/view/40801.htm

需要知道:

(1) GBK采用双字节表示,总体编码范围为8140-FEFE,首字节在81-FE 之间,尾字节在40-FE 之间。GBK的中文编码是双字节来表示的,英文编码是用ASCII码表示的,既用单字节表示。但GBK编码表中也有英文字符的双字节表示形式,所以英文字母可以有两种GBK表示方式。为区分中文,将其最高位都定成1。英文单字节最高位都为0。当用GBK解码时,若高字节最高位为0,则用ASCII码表解码;若高字节最高位为1,则用GBK编码表解码。
(2) UTF-8是UNICODE的一种变长字符编码又称万国码,它用1到6个字节编码UNICODE字符。
(3) Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。码位就是可以分配给字符的数字。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。


在Linux下如何查看文件的编码方式:

方法1:使用 vi 编辑器

我们通常是在Windows下写好代码然后再将文件上传到Linux下,那么可能会遇到文件编码转换的问题。Windows默认的文件格式是GBK(gb2312)编码,而Linux一般都是UTF-8编码。

在Linux下可以通过vi编辑器来查看文件的编码以及对文件进行编码转换:
在vi中可以直接查看文件编码:
:se fileencoding
在vi中可以直接转换文件编码:
:se fileencoding=utf-8
:se fileencoding=gbk
本文使用gbk的编码格式。

PS:
vi中有四个跟字符编码方式有关的选项:
(1) encoding        vi内部使用的字符编码方式
(2) fileencoding    vi中当前编辑的文件的字符编码方式
(3) fileencodings   vi自动探测fileencoding的顺序列表
(4) termencoding    vi所工作的终端的字符编码方式

这些选项可能的取值请参考vi在线帮助 :help encoding-names

在vi中可以通过以下两个命令查看字符的十六进制来确认当前的编码:
(1) ga
显示光标下字符的ASCII数值,十六进制,八进制数值。
(2) :%!xxd
用十六进制方式显示和编辑文件。
例如:
:%!xxd 整个文件以十六进制方式显示
:3!xxd 文件中第3行以十六进制方式显示
:%!od 隐藏右侧的文本列内容
注意:编辑完毕后,要使用命令 !xxd -r 将修改后的十六进制内容转换回来,否则修改后的十六进制的内容将被当作普通文本对待。

方法2:使用 od 命令

cat file | od -x
若使用不同的文件编码,可以查看文件实际存储数据是不同的。

方法3:使用 xxd 命令

xxd file | less

方法4:使用 hexdump 命令

hexdump -C file | less

测试代码

/* 
 * 防止串尾汉字(GBK)出现乱码
 * gerryyang
 * 2012-07-14
 */

#include <stdio.h>
#include <string.h>
#include <string>
using std::string;

/*
 * 功能:计算GBK中文串合理被截断的长度以防止串尾汉字出现乱码
 * @para s: 含有GBK编码字符串的头指针
 * @para iLeft: 函数外部能够使用的缓冲区大小
 * @para ret: 函数返回字符串s合理被截取的长度
 */

int GbkSubString(const char *s, int iLeft)
{
    int len = 0, i = 0;

	if( s == NULL || *s == 0 || iLeft <= 0 ) 
		return(0);

    while( *s )
    {
        if( (*s & 0x80) == 0 )
        {
            i ++;
            s ++;
            len ++;
        }
        else
        {
            if( *(s + 1) == 0 ) break;

            i += 2;
            s += 2;
            len += 2;
        }

        if( i == iLeft ) break;
        else if( i > iLeft )
        {
            len -= 2;
            break;
        }
    }

    return(len);
}


int main(int argc, char **argv)
{
	char szBuf[10] = {0};
	string str = "123abc你好";

	/* 
	 * [1] 未处理中文截断的情况
	 */
	snprintf(szBuf, sizeof(szBuf), "%s", str.c_str());
	printf("szBuf: %s\n", szBuf);

	/*
	 * [2] 处理中文可能被截断的情况
	 */
	memset(szBuf, 0x0, sizeof(szBuf));
	int iBufLeftLen = sizeof(szBuf)-1;

	// 计算合法的长度为iGbkValidLen
	int iGbkValidLen = GbkSubString(str.c_str(), iBufLeftLen);
	puts("");
	printf("str被合理截取的长度为: %d\n", iGbkValidLen);
	snprintf(szBuf, iGbkValidLen + 1, "%s", str.c_str());
	printf("szBuf: %s\n", szBuf);


	return 0;
}

/* 
g++ -Wall -g -o test code_test.c

output:
szBuf: 123abc你?

str被合理截取的长度为: 8
szBuf: 123abc你
*/

更多参考:

[1] Linux下文件中文乱码的一些情况
[2] vim encoding and font  
[3] vim 字符编码设置
[4] 谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词  
http://blog.csdn.net/fmddlmyy/article/details/372148
[5] 浅谈文字编码和 Unicode(上)
http://blog.csdn.net/fmddlmyy/article/details/1510189
[6] 浅谈文字编码和 Unicode(下)
http://blog.csdn.net/fmddlmyy/article/details/1510193
[7] 字符集GBK和UTF8的区别说明
http://space.itpub.net/55022/viewspace-713901
[8] 常见字符编码和编码头BOM
http://xouou.iteye.com/blog/1337417


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值