Linux下/Qt UTF-8转GB2312

本文介绍了解决热敏打印机在Linux+Qt环境下打印中文出现乱码的问题。通过三种方法探讨了编码转换过程,包括直接更改文件编码、使用iconv进行编码转换以及利用Qt内置的QTextCodec实现编码转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.背景

项目需要用到热敏打印机,控制接口为串口(RS232),运行环境为Linux+Qt。在此之前,在MCU平台的实时系统(RT-Thread)上已经实现出正确的打印功能,原则上把代码移植过来,调整下打印位置、字体大小等即可。代码移植后,运行结果是英文、数字打印正常,中文打印则出现乱码。现有的配置情况是,热敏打印机提供的英文、数字字库是ASII格式,中文字库是GB2312格式,平台控制端采用UTF-8编码格式,C++代码编写。因此基本可以确定是编码问题。

2.处理方法1——简单粗暴,将文件格式由UTF-8转为ANSI(windows只有这个格式选项,在简体中文系统下,ANSI代表GB2312)

鉴于之前开发的项目,团队成员之间或者其他电脑查阅源码工程时,都会莫名其妙的乱码,所以从此项目开始,所有源码编码的都采用UTF-8的保存格式(这个可以Qt Creator中设置),打印内容文件也是UTF-8保存。在此之前,记得上一东家同事的文件格式采用ANSI时,无需作编码转换即可正常打印中文。本人也做个尝试,将打印内容的文件改为ANSI格式(windows下用记事本打开->另存为->选择ANSI->保存)。结果很遗憾,打印还是乱码。也许是设置或者某些细节问题,这个我没有深入研究,因为比较感兴趣的是编码转换问题。

结论:该方法本人未能成功实现,后续再深入研究,欢迎大家分享经验。

3.处理方法2——iconv

iconv基于GPL公开源代码,是GNU项目的一部分,在各种Unix-like操作系统下很容易编译和使用,从网络上也能找到大牛介绍或者利用该函数写的编码转换源码【1】。所以,本人首先在Linux下,用C编写程序,调用该函数实现转换,然后再移植到Qt下面用C++方式实现,Linux下经过测试可以正常打印。关键代码如下:

保存为C源文件时,必须以UTF-8的格式。

#include <stdio.h>   /* 标准输入、输出定义 */
#include <unistd.h>  /* UNIX 标准函数定义 */
#include <fcntl.h>   /* 文件控制定义 */
#include <errno.h>   /* 错误编号定义 */
#include <termios.h> /* POSIX 终端控制定义 */
#include <string.h>  /* 字符串函数定义 */
#include <iconv.h>
#define SERIAL_PORT "/dev/ttySAC3" //ARM串口端口
#define BAUDRATE B9600   //9600bps
#define MAX_SIZE 255


/** \brief 打开串行端口一
 *  \brief 成功后返回文件描述符,或是失败后返回-1
 */
int code_convert(char *from_charset,char *to_charset,char *inbuf,int inlen,char *outbuf,int outlen)
{
 iconv_t cd;
 printf("in= %s\n",inbuf);
 char **pin = &inbuf;
 char **pout = &outbuf;
 char *old;
 old = outbuf;
 cd = iconv_open(to_charset,from_charset);
 if(cd==(iconv_t)-1)
 {
     	printf("iconv open failed!\n");
     	perror("iconv_open");
    	 return -1;
 }
 memset(outbuf,0,outlen);
 if (iconv(cd,pin,(size_t*)&inlen,pout,(size_t*)&outlen) == -1)
 {
    	printf("iconv failed!\n");
    	perror("iconv");
 }
 	iconv_close(cd);
 	printf("out= %s\n",&outbuf);
 	return outbuf - old;
}

int  gb2312_utf8(char *inbuf,int inlen,char *outbuf,int outlen)
{
 	return code_convert("gb2312","utf-8",inbuf,inlen,outbuf,outlen);
}


int  utf8_gb2312(char *inbuf,int inlen,char *outbuf,int outlen)
{
	 return code_convert("utf-8","gb2312",inbuf,inlen,outbuf,outlen);
}


int  gb2312_ucs2(char *inbuf,int inlen,char *outbuf,int outlen)
{
	 return code_convert("gb2312","ucs-2",inbuf,inlen,outbuf,outlen);
}

int  ucs2_gb2312(char *inbuf,int inlen,char *outbuf,int outlen)
{
 	return code_convert("ucs-2","gb2312",inbuf,inlen,outbuf,outlen);
}

int open_port()
{
    int fd; /* 端口文件描述符 */
    fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1)
    {
 	perror("open_port: Unable to open /dev/ttyS0 - ");
    }
   
    return (fd);
}

void set_port(int fd)
{
    struct termios options;
    int flag;
   
    tcgetattr(fd, &options);
    fcntl(fd, F_SETFL, 0);
    cfsetispeed(&options, BAUDRATE);
    cfsetospeed(&options, BAUDRATE);
   
    options.c_cflag |=  CREAD;
    /* Set to 8N1 */
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8; 
    /* RAW */
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);   
    options.c_oflag &= ~OPOST;
    /* For read data from printer */
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 1;   
    flag = tcsetattr(fd, TCSANOW, &options);
    if (flag < 0)
    {
 	perror("set_port: Setting port /dev/ttyS0 error - ");
    }
}
void close_port(int fd)
{
    close(fd);
}

void print(int fd, char* sent)
{
      int len = strlen(sent);
      char ToGB2312[100] = {0x00};
      utf8_gb2312(sent,strlen(sent),ToGB2312,len); 
      write(fd, &ToGB2312, strlen(ToGB2312));
}

void print_line(int fd,  char* sent)
{
      int len = strlen(sent);
      char ToGB2312[100] = {0x00};
      utf8_gb2312(sent,strlen(sent),ToGB2312,len); 
      ToGB2312[strlen(ToGB2312)] = 0x0a;
      write(fd, &ToGB2312, strlen(ToGB2312));
}

//走纸
void feed(int fd, int num)
{
	unsigned char ch_feed = 0x0a;
	int i;
	for (i=0;i<num;i++)
	{
		write(fd,&ch_feed,1);
	}
}

//热敏打印机条码打印,输入的为数字
void barcode(int fd, char* bar)
{	
	char ins[3] = {0x1d, 0x6b, 0x45}; //指令和条形码类型
	char hex_len[1]; //条形码长度
	hex_len[0] = strlen(bar);
	char ToGB2312[50] = {0x00};
	char all[100] = {0x00};
	write(fd,&ins,strlen(ins));
	write(fd,&hex_len,1);
	utf8_gb2312(bar,strlen(bar),ToGB2312,strlen(bar)); 
	write(fd,&ToGB2312,strlen(ToGB2312));
}

int main(int argc, char *argv[])
{
     int fd, i;
     unsigned char convert[100] = {0x00};

     fd = open_port();
     set_port(fd);
     print(fd,"我爱中国\x0A");
     print_line(fd,"=======================");
     print(fd,"Acuity\x0a");
     barcode(fd,"1234567");
     print_line(fd,"1234567");
     feed(fd,5);
     close_port(fd);
   
     return 0;
}
	printf("iconv open failed!\n");
     	perror("iconv_open");
    	 return -1;
 }
 memset(outbuf,0,outlen);
 if (iconv(cd,pin,(size_t*)&inlen,pout,(size_t*)&outlen) == -1)
 {
    	printf("iconv failed!\n");
    	perror("iconv");
 }
 	iconv_close(cd);
 	printf("out= %s\n",&outbuf);
 	return outbuf - old;
}

int  gb2312_utf8(char *inbuf,int inlen,char *outbuf,int outlen)
{
 	return code_convert("gb2312","utf-8",inbuf,inlen,outbuf,outlen);
}


int  utf8_gb2312(char *inbuf,int inlen,char *outbuf,int outlen)
{
	 return code_convert("utf-8","gb2312",inbuf,inlen,outbuf,outlen);
}


int  gb2312_ucs2(char *inbuf,int inlen,char *outbuf,int outlen)
{
	 return code_convert("gb2312","ucs-2",inbuf,inlen,outbuf,outlen);
}

int  ucs2_gb2312(char *inbuf,int inlen,char *outbuf,int outlen)
{
 	return code_convert("ucs-2","gb2312",inbuf,inlen,outbuf,outlen);
}

int open_port()
{
    int fd; /* 端口文件描述符 */
    fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1)
    {
 	perror("open_port: Unable to open /dev/ttyS0 - ");
    }
   
    return (fd);
}

void set_port(int fd)
{
    struct termios options;
    int flag;
   
    tcgetattr(fd, &options);
    fcntl(fd, F_SETFL, 0);
    cfsetispeed(&options, BAUDRATE);
    cfsetospeed(&options, BAUDRATE);
   
    options.c_cflag |=  CREAD;
    /* Set to 8N1 */
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8; 
    /* RAW */
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);   
    options.c_oflag &= ~OPOST;
    /* For read data from printer */
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 1;   
    flag = tcsetattr(fd, TCSANOW, &options);
    if (flag < 0)
    {
 	perror("set_port: Setting port /dev/ttyS0 error - ");
    }
}
void close_port(int fd)
{
    close(fd);
}

void print(int fd, char* sent)
{
      int len = strlen(sent);
      char ToGB2312[100] = {0x00};
      utf8_gb2312(sent,strlen(sent),ToGB2312,len); 
      write(fd, &ToGB2312, strlen(ToGB2312));
}

void print_line(int fd,  char* sent)
{
      int len = strlen(sent);
      char ToGB2312[100] = {0x00};
      utf8_gb2312(sent,strlen(sent),ToGB2312,len); 
      ToGB2312[strlen(ToGB2312)] = 0x0a;
      write(fd, &ToGB2312, strlen(ToGB2312));
}

//走纸
void feed(int fd, int num)
{
	unsigned char ch_feed = 0x0a;
	int i;
	for (i=0;i<num;i++)
	{
		write(fd,&ch_feed,1);
	}
}

//热敏打印机条码打印,输入的为数字
void barcode(int fd, char* bar)
{	
	char ins[3] = {0x1d, 0x6b, 0x45}; //指令和条形码类型
	char hex_len[1]; //条形码长度
	hex_len[0] = strlen(bar);
	char ToGB2312[50] = {0x00};
	char all[100] = {0x00};
	write(fd,&ins,strlen(ins));
	write(fd,&hex_len,1);
	utf8_gb2312(bar,strlen(bar),ToGB2312,strlen(bar)); 
	write(fd,&ToGB2312,strlen(ToGB2312));
}

int main(int argc, char *argv[])
{
     int fd, i;
     unsigned char convert[100] = {0x00};

     fd = open_port();
     set_port(fd);
     print(fd,"我爱中国\x0A");
     print_line(fd,"=======================");
     print(fd,"Acuity\x0a");
     barcode(fd,"1234567");
     print_line(fd,"1234567");
     feed(fd,5);
     close_port(fd);
   
     return 0;
}

 

插曲:在Linux执行该程序时,提示“invalid argument”,后面通过强大的优快云解决【2】。

4.处理方法3

将第二种方法移植,修改为C++描述,并置于Qt下,编译通过。但执行失败,在打开(iconv_open)iconv句柄时提示“invalid argument”,而此时的libc库已经更新,方法2可以成功执行,在Qt下为什么失败???继续换方法!后面想想,Qt应该有相关封装好的编码转换函数,果不其然,真的有,后面就轻松实现了。关键代码如下,打印内容文件依然需要保存UTF-8格式保存,字符参数采用Qt QString格式,或者C++的string都可以,比用C的char方便多了。

inline int UTF82GB2312(const QString &inStr,char *outbuf, int *outlen)
{
    QTextCodec *gbk = QTextCodec::codecForName("GB2312");
    QTextCodec *utf8 = QTextCodec::codecForName("UTF-8");
    char        *p;

    QByteArray byte_utf  = gbk->fromUnicode(inStr);
    int byte_utf_size = byte_utf.size();
    p = byte_utf.data();
    while(byte_utf_size > 0)
    {
        byte_utf_size --;
        *outbuf++ = *p++;
    }
    *outbuf = 0;
    return 0;
}
//UTT-8 to GB2312
int peripherals::utf8_gb2312(char *inbuf, int inlen, char *outbuf, int outlen)
{
    return code_convert("utf-8","gb2312",inbuf,inlen,outbuf,outlen); 		//此方式为调用处理方法2中的函数,但执行失败。
}
//UTT-8 to GB2312
int peripherals::utf8_gb2312(QString &instr, char *outbuf, int *outlen)
{
    return UTF82GB2312(instr,outbuf,outlen);
}

//UTT-8 to GB2312
int peripherals::utf8_gb2312(QString &instr, char *outbuf, int *outlen)
{
    return UTF82GB2312(instr,outbuf,outlen);
}


参考:

[1] http://blog.youkuaiyun.com/sealyao/article/details/5043138
[2] http://blog.youkuaiyun.com/violet089/article/details/51568667

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Acuity.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值