Qt编程进阶(93):字符串编码格式转换

在不同操作系统之间传输字符串数据时,经常碰到因字符串的格式编码不同造成显示乱码的现象,这是由于不同操作系统使用的编码格式不一致带来的问题。本实例主要就Qt应用中经常碰到的字符串转换成Unicode、Utf8和字符内码3种编码格式的问题展开分析,如下图所示。

在实例中,用户在源字符串的编辑框中输入需要转换的字符串,可以是字符或汉字,单击“转换”按钮后,程序自动将该字符串转换成Unicode、Utf8和字符内码3种编码格式的二进制显示,即在内存中的实际存储方式,并显示不同编码格式占用的字节长度。

几种编码格式

在分析实例之前,首先对几种编码格式作一些介绍,方便大家理解。

Unicode是统一表示各种语言字符的编码标准,支持跨平台、跨程序、跨语言。它将世界上所有的字符都映射成一个数字,如“汉”为0x6C49, Unicode是一种2字节编码, 由两个字节的大小存储一个字符。

Unicode规范中推荐使用的标记字节顺序的方法是BOM (Byte Order Mark),根据内存中存放的字节顺序分为Little-Endian和Big-Endian, Little-Endian采用高值存放在高地址字节,低值存放在低地址字节的方式;Big-Endian采用高值存放在低地址字节,低值存放在高地址字节的方式。不同的体系结构采用的字节存放顺序是不一样的,如x86中通常以Little-Endian方式存放,而PowerPC中通常以Big-Endian存放。

通常来讲,Unicode指的是Little-Endian,如“汉”的Unicode值为0x6C49,实际在内存中,6C放在高地址字节,49放在低地址字节,即49 6C。对于BigEndian Unicode,“汉”在内存中,49放在高地址字节,6C放在低地址字节,即6C 49。

UTF-8是UTF (UCS Transformation Format)规范规定的一种Unicode编码表示方式,另外还有UTF-16等。UTF-8是以8位为单元对Unicode进行编码,兼容ASCII字符;UTF-16是以16位对Unicode进行编码,与ASCII字符不兼容,对于小于0x10000的Unicode, UTF-16编码就等于Unicode编码所对应16位无符号整数。UTF-8是以字节流的方式表示,没有字节顺序的问题。UTF-8与Unicode编码之间的转换规则如下:

例如,“汉”字的Unicode为0x6C49,位于0x0800〜OxFFFF之间,对应的UTF-8字节流为1110xxxx 10xxxxxx 10xxxxxx,UTF-8编码采用三字节表示,0x6C49的二进制为0x0110110001001001,将这16位分别替换上面的X,可以得到11100110 10110001 10001001,即E6B189,这就是中文“汉”字的UTF-8编码。

字符内码指的是用来代表字符的内码,包括单字节内码和双字节编码,单字节内码就是ASCII码,可以表示256个字符编码;双字节内码就是ANSI,可以表示65000个字符编码。对于简体中文编码GB2312,实际上对应的是ANSI的一个代码页936。ANSI有很多代码页,使用不同代码页的内码无法在其他代码页正常显示,如繁体中文、日文都对应ANSI的一个代码页,这些内码无法在简体中文GB2312的平台上正常显示。对于GB2312来说,用一字节表示一个字符,每一个汉字由两个字符构成,因此每一个汉字占两个字节。

实例代码

转换函数的代码如下:

void Widget::on_btnConvert_clicked()
{
  ui->edtUnicode->clear(); // 首先清空各控件的内容
  ui->edtBigEndian->clear();
  ui->edtUtf8->clear();
  ui->edtLocal->clear();
  QString source=ui->edtSrc->text(); // 获得用户输入的源字符串
  const QChar *u=source.unicode(); // (a)
  // 获得Unicode编码
  ui->edtUnicodeLen->setText(QString::number(source.length()*2)); // (b)
  for(int i=0;i<source.length();i++)  {
    const ushort unicode=u[i].unicode(); // (c)
    ui->edtUnicode->insert(QString::number(unicode%256,16)+" "); // (d)
    ui->edtUnicode->insert(QString::number(unicode/256,16)+" ");
  }
  // 获得 Big-Endian Unicode 编码
  ui->edtBigEndianLen->setText(QString::number(source.length()*2));
  for(int i=0;i<source.length();i++)  {
    const ushort unicode=u[i].unicode();
    ui->edtBigEndian->insert(QString::number(unicode/256,16)+" "); // (e)
    ui->edtBigEndian->insert(QString::number(unicode%256,16)+" ");
  }
  // Utf8
  QByteArray b=source.toUtf8(); // (f)
  ui->edtUtf8Len->setText(QString::number(b.length())); // (g)
  for (int i=0;i<b.length();i++) {
    const unsigned char a=b[i];
    ui->edtUtf8->insert(QString::number(a,16)+" "); // (h)
  }
  // Local
  QByteArray bl=source.toLocal8Bit(); // (i)
  ui->edtLocalLen->setText(QString::number(bl.length()));
  for (int i=0;i<bl.length();i++) {
    const unsigned char a=bl[i];
    ui->edtLocal->insert(QString::number(a,16)+" ");
  }
}

其中:

  • (a):获得QString对象的Unicode编码,这是一个QChar*对象,QChar提供了一种16位表示Unicode的方式,使用两个字节表示一个字符。
  • (b):获得源字符串的Unicode编码的长度,由于Unicode采用两个字节来表示一个字符,因此,这里通过源字符串长度的两倍得到Unicode的长度。
  • (c):通过QChar的unicode()方法获得每一个字符的Unicode编码值,这是一个ushort类型的值,占两个字节。
  • (d):分别获得ushort值的低值和髙值字节,由于Unicode采用的是Little-Endian方式,因此,低值字节存放在低地址字节上,高值字节存放在高地址字节上。
  • (e):分别获得ushort值的高值和低值字节,由于这里采用的是Big-Endian方式,因此,髙值字节存放在低地址字节上,低值字节存放在高地址字节上。
  • (f):通过QString的toUtf8()方法得到源字符串的UTF-8编码序列,这是一个QByteArray对象,QByteArray提供了一种字节流的表示方式。
  • (g):获得该字节流的长度信息,这就是源字符串的UTF-8编码所占用的字节长度。
  • (h):分别对字节流的每一个字节按十六进制的形式显示,UTF-8没有字节顺序的问题,因此QByteArray中存放的字节流顺序就是内存中存放的字节顺序。
  • (i):获得源字符串的字符内码编码序列,这也是一个QByteArray对象,通过QString的toLocal8Bit()可以获得本地8位字符内码编码的字节流。

——————————————————

对于本文实例完整代码有需要的朋友,可关注并在评论区留言!

举报
评论 0