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
#include
#include
#include
#include
#include
#include
#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 |&#61; CREAD;/* Set to 8N1 */options.c_cflag &&#61; ~PARENB;options.c_cflag &&#61; ~CSTOPB;options.c_cflag &&#61; ~CSIZE;options.c_cflag |&#61; CS8; /* RAW */options.c_lflag &&#61; ~(ICANON | ECHO | ECHOE | ISIG); options.c_oflag &&#61; ~OPOST;/* For read data from printer */options.c_cc[VMIN] &#61; 0;options.c_cc[VTIME] &#61; 1; flag &#61; 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 &#61; strlen(sent);char ToGB2312[100] &#61; {0x00};utf8_gb2312(sent,strlen(sent),ToGB2312,len); write(fd, &ToGB2312, strlen(ToGB2312));
}void print_line(int fd, char* sent)
{int len &#61; strlen(sent);char ToGB2312[100] &#61; {0x00};utf8_gb2312(sent,strlen(sent),ToGB2312,len); ToGB2312[strlen(ToGB2312)] &#61; 0x0a;write(fd, &ToGB2312, strlen(ToGB2312));
}//走纸
void feed(int fd, int num)
{unsigned char ch_feed &#61; 0x0a;int i;for (i&#61;0;i
void barcode(int fd, char* bar)
{ char ins[3] &#61; {0x1d, 0x6b, 0x45}; //指令和条形码类型char hex_len[1]; //条形码长度hex_len[0] &#61; strlen(bar);char ToGB2312[50] &#61; {0x00};char all[100] &#61; {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] &#61; {0x00};fd &#61; open_port();set_port(fd);print(fd,"我爱中国\x0A");print_line(fd,"&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;");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) &#61;&#61; -1){printf("iconv failed!\n");perror("iconv");}iconv_close(cd);printf("out&#61; %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 &#61; open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);if (fd &#61;&#61; -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 |&#61; CREAD;/* Set to 8N1 */options.c_cflag &&#61; ~PARENB;options.c_cflag &&#61; ~CSTOPB;options.c_cflag &&#61; ~CSIZE;options.c_cflag |&#61; CS8; /* RAW */options.c_lflag &&#61; ~(ICANON | ECHO | ECHOE | ISIG); options.c_oflag &&#61; ~OPOST;/* For read data from printer */options.c_cc[VMIN] &#61; 0;options.c_cc[VTIME] &#61; 1; flag &#61; 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 &#61; strlen(sent);char ToGB2312[100] &#61; {0x00};utf8_gb2312(sent,strlen(sent),ToGB2312,len); write(fd, &ToGB2312, strlen(ToGB2312));
}void print_line(int fd, char* sent)
{int len &#61; strlen(sent);char ToGB2312[100] &#61; {0x00};utf8_gb2312(sent,strlen(sent),ToGB2312,len); ToGB2312[strlen(ToGB2312)] &#61; 0x0a;write(fd, &ToGB2312, strlen(ToGB2312));
}//走纸
void feed(int fd, int num)
{unsigned char ch_feed &#61; 0x0a;int i;for (i&#61;0;i
void barcode(int fd, char* bar)
{ char ins[3] &#61; {0x1d, 0x6b, 0x45}; //指令和条形码类型char hex_len[1]; //条形码长度hex_len[0] &#61; strlen(bar);char ToGB2312[50] &#61; {0x00};char all[100] &#61; {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] &#61; {0x00};fd &#61; open_port();set_port(fd);print(fd,"我爱中国\x0A");print_line(fd,"&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;");print(fd,"Acuity\x0a");barcode(fd,"1234567");print_line(fd,"1234567");feed(fd,5);close_port(fd);return 0;
}
插曲&#xff1a;在Linux执行该程序时&#xff0c;提示“invalid argument”&#xff0c;后面通过强大的CSDN解决【2】。
4.处理方法3
将第二种方法移植&#xff0c;修改为C&#43;&#43;描述&#xff0c;并置于Qt下&#xff0c;编译通过。但执行失败&#xff0c;在打开&#xff08;iconv_open&#xff09;iconv句柄时提示“invalid argument”&#xff0c;而此时的libc库已经更新&#xff0c;方法2可以成功执行&#xff0c;在Qt下为什么失败&#xff1f;&#xff1f;&#xff1f;继续换方法&#xff01;后面想想&#xff0c;Qt应该有相关封装好的编码转换函数&#xff0c;果不其然&#xff0c;真的有&#xff0c;后面就轻松实现了。关键代码如下&#xff0c;打印内容文件依然需要保存UTF-8格式保存&#xff0c;字符参数采用Qt QString格式&#xff0c;或者C&#43;&#43;的string都可以&#xff0c;比用C的char方便多了。
inline int UTF82GB2312(const QString &inStr,char *outbuf, int *outlen)
{QTextCodec *gbk &#61; QTextCodec::codecForName("GB2312");QTextCodec *utf8 &#61; QTextCodec::codecForName("UTF-8");char *p;QByteArray byte_utf &#61; gbk->fromUnicode(inStr);int byte_utf_size &#61; byte_utf.size();p &#61; byte_utf.data();while(byte_utf_size > 0){byte_utf_size --;*outbuf&#43;&#43; &#61; *p&#43;&#43;;}*outbuf &#61; 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中的函数&#xff0c;但执行失败。
}
//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);
}
参考&#xff1a;
[1] http://blog.csdn.net/sealyao/article/details/5043138
[2] http://blog.csdn.net/violet089/article/details/51568667