精华内容
参与话题
问答
  • * 众所周知,计算机中数据的存储和传输的最小单位是字节(byte),一个ASCII 码占用 1 个字节, 每个字节为 8 个比特位(Bit);...* 压缩算法有很多,今天介绍一种比较好理解的贪心算法 - 霍夫曼编码

    一、概述

    1、算法简述

    • 众所周知,计算机中数据的存储和传输的最小单位是字节(byte),一个ASCII 码占用 1 个字节, 每个字节为 8 个比特位(Bit);例如,字符 ‘e’ 的二进制表示为 01100101;
    • 进程间通信传输字节流的过程中,为了节省带宽,往往会对传输的数据进行压缩。
    • 压缩算法有很多,今天介绍一种比较好理解的贪心算法 - 霍夫曼编码;
    • 霍夫曼编码的本质就是对每个出现过的 ASCII 字符,通过一个压缩字典,映射成另一个字符,映射后的字符是二进制比特串:001、0101、00 等等;
    • 解压缩就是这个过程的逆过程;

    2、引例

    源字节流

    • 首先,一个字符串 “HelloWorld”,在没有进行压缩的情况下采用 ASCII 编码,占用字节总数为 10,即 10 * 8 = 80 个比特位。

    压缩字典

    • 然后,通过霍夫曼算法生成压缩字典如下(具体生成过程是霍夫曼树的构造过程,下文会详细讲述):
    字符 压缩前编码(ASCII) 压缩后编码
    d 01100100 001
    e 01100101 010
    l 01101100 10
    o 01101111 111
    r 01110010 011
    H 01001000 1101
    W 01010111 000

    编码

    • 继而,遍历源字节流,对对应的字符进行编码替换,得到新的比特流;
    H e l l o W o r l d
    1101 010 10 10 111 000 111 011 10 001

    规范化

    • 最后,将比特流再转换成字节流,由于编码后比特流长度不一定是8的倍数,所以最后的 XXXX 是补足位(补足位具体的值下文会接着探讨);
    第1字节 第2字节 第3字节 第4字节
    11010101 01011100 01110111 0001XXXX
    • 观察一下就会发现,每个压缩后的编码长度都是小于8的,所以压缩后的总位数一定是会小于压缩前的;压缩前 80 个比特位,压缩后 32 个比特位,压缩率为 40%;

    解码

    • 解码过程是霍夫曼树的路径查找过程,了解霍夫曼树的构造过程,这个解码过程就一目了然了,下文也会详细介绍;

    二、概念

    1、变长编码

    • 每个 ASCII 字符占用字节数都为1,所以它是一种定长编码;区别于定长编码,霍夫曼编码是一种变长编码,即每个 ASCII 码进行编码映射后的二进制比特串的长度不相等;它是根据字符出现的概率来构造平均长度最短的编码,换言之,如果一个字符在文档中出现的次数多,那么它的映射后的二进制比特串就应该相对较短;反正,如果出现的次数少,那么它的映射后的二进制比特串就应该相对较长;

    2、前缀编码

    • 前缀编码的含义是:编码集合中,任意两个编码 A 和 B,A 不能是 B 的前缀;
    • 这是为了保证在传输过程中,不用增加分隔符来区分两个编码值;
    • 霍夫曼编码是一种前缀编码,为什么呢?下文继续讲;

    3、比特串

    • 简单认为就是数组,数组的最小单位是二进制比特位,也就是只有两种值:0或1;

    4、压缩率

    • 压缩率 = 压缩后字节数 / 压缩前字节数;
    • 压缩率的值越小越好;
    • 压缩率取决于字符集合,字符集合越大,压缩率越大(压缩率越小越好),256个字符都有的话,等于不压缩,因为基本所有的编码后的字符长度都是8,和原字符一致,甚至有可能更高;
    • 如果压缩字符里面存在中文,不适合采用 霍夫曼压缩 (还是基于字符集合,中文会占用负数的 ASCII 码);
    • 了解算法以后再来看压缩率,会更加直观;
    • 如果所有字符都平均分布,则字符个数和压缩率的实测结果如下;
    字符个数 压缩率(越低越好)
    1 12.50%
    2 18.77%
    4 28.14%
    8 39.08%
    16 50.80%
    32 62.91%
    64 75.21%
    128 87.61%
    256 100.00%

    三、算法详解

    1、算法要求

    • 1、编码后的码值是二进制的比特串;
    • 2、编码后的任意两个比特串 A 和 B, A 不能是 B 的前缀,这是为了保证在传输过程中,不用增加分隔符来区分两个编码值;
    • 3、出现频率高的字符,编码后的比特串相对较短;出现频率低的字符,编码后的比特串相对较长;
    • 4、编码完要保证编码总长度最小;

    2、算法简述

    • 1、统计字节流中出现的字符次数(频率);
    • 2、按照字符频率放入一个小顶优先队列中(小顶堆);
    • 3、如果优先队列中只剩一个结点,则这个结点为霍夫曼树的根结点;否则,取出频率最小和次小的两个,进行合并,产生一个新的结点再塞回优先队列中;其中,这个新的结点的左右子树分别是频率最小和次小的那两个结点,新的结点的频率值为两个子树频率值的和;
    • 4、重复3的过程,构造出来的树就是霍夫曼树;

    压缩前的串

        HelloWorld
    

    频次统计

    字符 压缩前编码 出现频次
    d 01100100 1
    e 01100101 1
    l 01101100 3
    o 01101111 2
    r 01110010 1
    H 01001000 1
    W 01010111 1
    • 启发:出现频次多的字符,编码后的比特串应该尽量短;

    霍夫曼树构造

    0
    1
    0
    0
    0
    1
    1
    0
    1
    1
    0
    0
    1
    1
    3
    4
    6
    10
    l
    2
    W
    d
    2
    e
    r
    1
    ......
    H
    o
    • 观察可得:
      1. 叶子结点为每个未编码的 ASCII 字符,非叶子结点上的数字代表的是该结点为根的子树下所有字符的频率和;
      1. 树上任何一个非叶子结点一定有左右两棵子树(这是由构造算法决定的);
      1. 左子树的边权为0,右子树的边权为1;
      1. 根结点到叶子结点路径上的边权集合就是对应字符的编码后的比特串,比如字符 r 编码后的比特串为 011;
      1. 图中的 … 代表其他字符(总共 256 个字符集合),这棵树深度还很深,因为其它字符没有出现,所以频率都为0;

    霍夫曼编码

    • 根结点到叶子结点的树边上的路径,就是对应字符的编码值,也正是上文提到的压缩字典;
    字符 压缩前编码 出现频次 压缩后编码
    d 01100100 1 001
    e 01100101 1 010
    l 01101100 3 10
    o 01101111 2 111
    r 01110010 1 011
    H 01001000 1 1101
    W 01010111 1 000
    其它 - 0 -

    霍夫曼压缩

    • 然后就比较好理解了,通过映射关系将每个字符替换为二进制比特串;由于计算机中存储和传输的基本单位是字节,所以替换过程是通过位运算来完成的,具体实现方式下文会详细介绍;
    H e l l o W o r l d
    1101 010 10 10 111 000 111 011 10 001
    第1字节 第2字节 第3字节 第4字节
    11010101 01011100 01110111 0001XXXX

    不可达字符

    • 然后来分析下上文提到的 XXXX,也就是最后一个字节的补齐字符;
    • XXXX 必定是不会出现在压缩后编码集合中的字符,仔细想一下,编码集合256个字符,霍夫曼树中深度最深的那个叶子结点,它的编码长度一定是超过 8 的(想想为什么?),所以只要取这个比特串的高 8 位作为不可达字符即可;
    • 根本原因就是 前缀编码,只要是一个编码的前缀,必定不在编码集合中,它的前缀可以放心用来做 不可达字符;

    霍夫曼解压缩

    • 1、准备一个指向霍夫曼树根结点的指针 searchNode;

    • 2、遍历被压缩串的每个比特位,如果是 0 ,则将 searchNode 指向它的左子树;如果是1,则将 searchNode 指向它的右子树;判断3;

    • 3、当 searchNode 为叶子结点,则说明这是一个完整的字符,提取出结点上的字符(ASCII 码值)写入到解压缩缓冲区,searchNode 继续指向 霍夫曼树的根结点,重复步骤2;

    • 如图,待解压串 110101010…,通过下面的霍夫曼树从根结点一直往下找 1101 后遇到叶子结点,所以解出来的第一个字符为 ‘H’;然后再回到根结点,周而复始,直到整个待解压串遍历完毕就获得了完整的源字节流;

    1
    1
    0
    1
    3
    4
    6
    10
    l
    2
    W
    d
    2
    e
    r
    1
    ......
    H
    o

    四、数据结构和接口设计

    1、压缩结构

    typedef struct CompressdData
    {
    	char* compress_result;                 
    	unsigned int capacity;
    	unsigned int size;
    	unsigned char _cache_bit;
     	unsigned char _cache_bit_index;
    } CompressdData;
    
    • i. compress_result 用于存储压缩或者解压缩的字节流,支持动态扩展;

    • ii. capacity 代表了 compress_result 实际在堆上的空间;

    • iii. size 代表压缩、或者解压缩的字节流的实际大小;

    • iv. _cache_bit 表示某次编码(压缩)过程中,没有写入 compress_result 的剩余部分(高位);

    • v. _cache_bit_index 代表 _cache_bit 的位数;

    • 例如,图中 11010101 组成一个字节,这时候 _cache_bit = 010, _cache_bit_index = 3;

    H e l l
    1101 010 10 10
    • 当加入一个字符o以后,_cache_bit = 010111,_cache_bit_index = 6;
    H e l l o
    1101 010 10 10 111

    2、编码字典

    struct huffman_encoded_data
    {
    	unsigned long long value;
    	unsigned char length;
    };
    
    • 编码字典 huffman_encoded_data[256] 存储了每个字符编码后的值,以及长度;
        比如 ASCII 值为 200 的字符,编码以后变成 17,二进制表示为 10001;
        那么 value 的值就是 17, length 的值为 5
    • 令当前需要编码的字符 i;
    • 编码后的值 V = huffman_encoded_data[i].value ;
    • 编码后的值 V 的长度 L = huffman_encoded_data[i].length;
    • 则需要写入 compress_result 的数据需要按照下标进行;
    长度范围 写入数据类型
    0< L <=8 L个Bit
    8< L <=16 L-8个Bit、1个byte
    16< L <=24 L-16个Bit、1个short
    24< L <=32 L-24个Bit、1个char、1个short
    32< L <=40 L-32个Bit、1个int
    40< L <=48 L-40个Bit、1个char,1个int
    48< L <=56 L-48个Bit、1个short、1个int
    56< L <=64 L-56个Bit、1个char、1个short,1个int

    3、编码写入

    i. 写入完整 Bit

    void writeChar_Internal(unsigned char value)
    {
    	if (size + 1 > capacity)
    	{
    		expand(capacity);
    	}
    	*(compress_result + size) = value;
    	size += 1;
    }
    
    • writeChar_Internal 这个接口表示写入完整的一个字节 value;
    • 并且在写入的过程中判断容量,超过容量就进行倍增;
    • expand 函数就是在堆上分配 2*capacity 的内存,然后将 compress_result 原来的数据拷贝到新的内存,再删掉原来的内存;

    ii. 写入非完整 Bit

    void writeBit(unsigned char value, int fromBit, int bitSize)
    {
    	int leftBitIndex = 8 - _cache_bit_index;
    	if (bitSize < leftBitIndex)
    	{
    		_cache_bit |= (((unsigned char)((value << fromBit) & _MINI_BIT_MASK[bitSize])) >> _cache_bit_index);
    		_cache_bit_index += bitSize;
    	}
    	else
    	{
    		_cache_bit |= (((unsigned char)(value << fromBit)) >> _cache_bit_index);
    
    		this->writeChar_Internal(_cache_bit);
    		fromBit += leftBitIndex;
    		bitSize -= leftBitIndex;
    		_cache_bit_index = bitSize;
    		_cache_bit = (((unsigned char)(value << fromBit)) & _MINI_BIT_MASK[bitSize]);
    	}
    }
    
    • writeBit 表示写入的比特位不一定是8位,而是 bitSize 位;
    • _MINI_BIT_MASK 是个二进制掩码,用来作 位与(&),去掉低位的0,如下;
    下标 二进制值 十进制值
    0 00000000 0
    1 10000000 128
    2 11000000 192
    3 11100000 224
    4 11110000 240
    5 11111000 248
    6 11111100 252
    7 11111110 254
    8 11111111 255
    • leftBitIndex 代表本次写入还可以写多少个Bit,这里的 left 是剩余的意思,不是,如果没有理解,很容易理解成,那后面就更难理解了;
    直接写入
    • 需要写入的位数 bitSize 小于 剩余位数 leftBitIndex,则直接写入 _cache_bit,并且更新 _cache_bit_index;
    • 为了更加容易理解,举个例子,某种情况下的变量值如下:
    变量类型 变量名 含义
    成员 _cache_bit_index 2 当前字节的2个位被占用了
    成员 _cache_bit 11000000 当前字节的2个被占用的位都是1
    传参 bitSize 3 写入的字节只需要3个位
    传参 value 00000101 需要写入的字节的三个位是 101
    传参 fromBit 5 8 减去写入的位数
    • 那么我们的目的就是要把 value 的3个位‘101’放到_cache_bit的2个‘11’后面;
    操作 功能
    a=(value << fromBit) 10100000 将101提到高位
    b = a & _MINI_BIT_MASK[bitSize] 10100000 剔除101以外的所有位
    c = b >> _cache_bit_index 00101000 空出两个位给_cache_bit
    _cache_bit = _cache_bit 位或 c 11101000 11和101合体
    超出字节
    • 需要写入的位数 bitSize 大于等于 剩余位数 leftBitIndex,则必须先写一个字节,然后再缓存 _cache_bit;
    • 还是举例说明吧;
    变量类型 变量名 含义
    成员 _cache_bit_index 2 当前字节的2个位被占用了
    成员 _cache_bit 11000000 当前字节的2个被占用的位都是1
    传参 bitSize 7 写入的字节需要7个位
    传参 value 00100101 需要写入的字节的7个位是 0100101
    传参 fromBit 1 8 减去写入的位数
    • 操作完的结果是 11010010 写入压缩数据,剩下的 10000000 写入_cache_bit;
    操作 功能
    a=(value << fromBit) 01001010 将0100101提到高位
    b = a>>_cache_bit_index 00010010 空出两个位给_cache_bit
    _cache_bit = _cache_bit 位或 b 11010010 11和00010010合体
    • 写入前半个字节:writeChar_Internal(_cache_bit);
    操作
    fromBit = fromBit + leftBitIndex 7
    bitSize = bitSize - leftBitIndex 1
    _cache_bit_index = bitSize 1
    a = (value << fromBit) 001001010000000
    _cache_bit = a & _MINI_BIT_MASK[bitSize] 10000000

    iii. 写入 byte

    • 写入 byte,其实就是写入8个位,当然,举一反三,写 short 和 int 也雷同;
    void writeChar(unsigned char value)
    {
    	writeBit(value, 0, 8);
    }
    

    iv. 写入 short

    void writeShort(unsigned short value)
    {
    	unsigned char hValue = (value >> 8) & 0xff;
    	unsigned char lValue = (value) & 0xff;
    	
    	writeChar(hValue);
    	writeChar(lValue);
    }
    

    iv. 写入 int

    void writeInt(unsigned int value)
    {
    	unsigned char value1 = (value >> 24) & 0xff;
    	unsigned char value2 = (value >> 16) & 0xff;
    	unsigned char value3 = (value >> 8) & 0xff;
    	unsigned char value4 = value & 0xff;
    
    	writeChar(value1);
    	writeChar(value2);
    	writeChar(value3);
    	writeChar(value4);
    }
    
    展开全文
  • java技术——中文编码知识总结

    万次阅读 多人点赞 2016-04-21 22:01:17
    为什么要编码呢?在计算机中存储信息的最小单位是1个字节,即8个bit。而我们平时要展现的符号太多,无法用一个字节来完全表示。所以我们构建了一个新的数据结构char,而从char到byte就必须有编码的过程。 在java...

    为什么要编码呢?在计算机中存储信息的最小单位是1个字节,即8个bit。而我们平时要展现的符号太多,无法用一个字节来完全表示。所以我们构建了一个新的数据结构char,而从char到byte就必须有编码的过程。

    在java中是如何编码的。

    例子代码:

     

    Source code    
    package net.mpos.lottery.distributorwrs.test;
     
    	/**
    	 * @author solo
    	 * 
    	 * @date 2013-9-7
    	 */
    	public class CodeTest {
     
    	    public static void toHex(char[] b) {
    	        for (int i = 0; i < b.length; i++) {
    	            System.out.printf("%x " , (int)b[i]);
    	        }
    	        System.out.println();
    	    }
     
    	    public static void toHex(byte[] b) {
    	        for (int i = 0; i < b.length; i++) {
    	            System.out.printf("%x " , b[i]);
    	        }
    	        System.out.println();
    	    }
     
    	    public static void encode() {
    	        String name = "I am 中文编码";
    	        toHex(name.toCharArray());
    	        try {
    	            byte[] iso8859 = name.getBytes("ISO-8859-1");
    	            System.out.println("ISO-8859-1:");
    	            toHex(iso8859);
    	            byte[] gb2312 = name.getBytes("GB2312");
    	            System.out.println("GB2312:");
    	            toHex(gb2312);
    	            byte[] gbk = name.getBytes("GBK");
    	            System.out.println("GBK:");
    	            toHex(gbk);
    	            byte[] utf16 = name.getBytes("UTF-16");
    	            System.out.println("UTF-16:");
    	            toHex(utf16);
    	            byte[] utf8 = name.getBytes("UTF-8");
    	            System.out.println("UTF-8:");
    	            toHex(utf8);
    	        } catch (Exception e) {
    	            e.printStackTrace();
    	        }
    	    }
     
    	    public static void main(String[] args) {
    	        String cn = "I am 中文编码";  // 这里存在编码转换: 将文件存储字节转成unicode存入String对象内存. 采用文件编码
     
    	        char[] charArray = cn.toCharArray();
    	        byte[] data = cn.getBytes();
     
    	        System.out.println("print char array : " + cn);
    	        toHex(cn.toCharArray());
     
    	        encode();
    	    }
    	}

     

    例子结果分析

    Source code    
    print char array : I am 中文编码
    49 20 61 6d 20 4e2d 6587 7f16 7801 
    49 20 61 6d 20 4e2d 6587 7f16 7801 
    ISO-8859-1:
    49 20 61 6d 20 3f 3f 3f 3f 
    GB2312:
    49 20 61 6d 20 d6 d0 ce c4 b1 e0 c2 eb 
    GBK:
    49 20 61 6d 20 d6 d0 ce c4 b1 e0 c2 eb 
    UTF-16:
    fe ff 0 49 0 20 0 61 0 6d 0 20 4e 2d 65 87 7f 16 78 1 
    UTF-8:
    49 20 61 6d 20 e4 b8 ad e6 96 87 e7 bc 96 e7 a0 81

    ISO-8859-1是单字节编码,中文会转化成3f的byte。3f也就是“?”字符。所以出现中文编程“?”的时候很可能就是误用了ISO-8859-1。

    GB2312和GBK字符集有一个从char到byte的码表,不同的字码就是从这个码表找到找到与每个字符对应的字节,然后拼装成byte数组。GBK的字符集>GB2312的字符集。所以GBK编码是兼容GB2312的编码的。
    UTF-16将char数组放大了一倍。但字节字符在高位补0.
    UTF-8对单字节字符仍用1个字节表示,对于汉字采用三个字节表示。
    展开全文
  • LDPC构造及编码

    千次阅读 2018-05-08 11:47:11
    LDPC码构造方法有很多,我所了解的如下:1. 随机构造法:首先随机生成一个满足要求的规则H矩阵,由于随机法无法满足无短环要求,所以至少还需要消除4环这一步骤。然而4环并不是那么好消除的,所以可能消除4环的效果...

    LDPC码构造方法有很多,我所了解的如下:

    1. 随机构造法:首先随机生成一个满足要求的规则H矩阵,由于随机法无法满足无短环要求,所以至少还需要消除4环这一步骤。然而4环并不是那么好消除的,所以可能消除4环的效果不怎样,导致的结果就是后面译码性能差。另外,消除四环的过程中很可能将H变为非规则码,所以这种方法构造的码往往是非规则码。消短环后很可能导致H出现很多列重低于3的情况,带来低列重码字。

    2. 组合数学完备循环差集构造法:这种方法构造的H是无4环规则码,且同一行列数要求下的H很多(是MACKAY或者欧式几何码的数量不能相比的),这种方法构造的码优点很多,觉得还不错。

    相关文章:LDPC码的一种循环差集构造法

    3. 对角线法:这种方法主要用于构造规则码(也可以用于不规则码)。

    相关文章:点击打开链接

    LDPC编码方法

    LDPC编码方法主要有两种,高斯消元法和近似下三角形法。

    1. 全下三角法:将已经得到的H矩阵通过高斯变换转换为全下三角形式,这样编码得到的是系统码,编码简单,但由于高斯变换会改变矩阵的稀疏性,而LDPC码的编码复杂度主要就是有H的稀疏性决定的,所以这种方法的编码复杂度很大,为O(n3),码长较短时还好说,码长很长时这样的复杂度受不了。(其中n为分组码的码长)

    相关文章:《LDPC码基础与应用》人民邮电出版社,贺鹤云 编著

    2. 系统编码法:也是通过高斯变换将得到的H转化为系统形式,这样编码简单,同样由于破坏了稀疏矩阵的稀疏性导致编码复杂度(主要是高斯变换过程复杂)很高,复杂度同上。

    3. 近似下三角法:将已经得到的H通过行列变换转化为近似下三角形,因为行列变换不改变矩阵的稀疏性,所以这种方法复杂度最低,也适用于长码。编码复杂度为O(n)。

    其中第二种方法,系统编码法通用性最好,但由于编码复杂度过高导致实用性不强;第三种方法,近似下三角形法,通用性良好,但受限于g尽可能小的情况下才能实现线性复杂度编码;第一种方法,采用初等行列变换将H转换为下三角形式的可能性比较小,所以一般采用高斯变换,这样一来编码复杂度很高。


    展开全文
  • 彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)

    万次阅读 多人点赞 2014-02-15 16:46:22
    默认编码:System(简体中文windows系统默认指的是GBK编码,即下拉框选项里的GBK/windows-936-2000/CP936/MS936/windows-936) 二、编码知识科普 Qt常见的两种编码是:UTF-8和GBK ★UTF-8:Unicode Transformat

    尊重作者,支持原创,如需转载,请附上原地址:https://blog.csdn.net/libaineu2004/article/details/19245205

    这篇文章有点长,内容有点多,如果时间急迫,可以直接翻页去末尾看结论。红色字体加粗的。(#^.^#)

     

    一、Qt Creator环境设置

    1、cpp或h文件从window上传到Ubuntu后会显示乱码,原因是因为ubuntu环境设置默认是utf-8,Windows默认都是GBK.

    我们使用的Windows系统本地字符集编码为GBK。

    2、Windows环境下,Qt Creator,菜单->工具->选项->文本编辑器->行为->文件编码->默认编码,常用的选项有以下几个:

    System(简体中文windows系统默认指的是GBK编码)

    GBK/windows-936-2000/CP936/MS936/windows-936

    UTF-8

     

    二、编码知识科普

    Qt常见的两种编码是:UTF-8和GBK
    ★UTF-8:Unicode TransformationFormat-8bit,允许含BOM,但通常不含BOM。是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码。UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。UTF-8编码的文字可以在各国支持UTF8字符集的浏览器上显示。如果是UTF8编码,则在外国人的英文IE上也能显示中文,他们无需下载IE的中文语言支持包。
    ★GBK是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBD大。GBK是GB2312的扩展,除了兼容GB2312外,它还能显示繁体中文,还有日文的假名。
    ★GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换:
    GBK、GB2312--Unicode--UTF8
    UTF8--Unicode--GBK、GB2312
    ★在简体中文windows系统下,ANSI编码代表GBK/GB2312编码,ANSI通常使用0x80~0xFF范围的2个字节来表示1个中文字符。0x00~0x7F之间的字符,依旧是1个字节代表1个字符。Unicode(UTF-16)编码则所有字符都用2个字节表示。

     

    三、编码转换

    Windows自带的记事本,无法查看UTF-8编码的文件到底有无BOM,需要使用其他文件编辑器,比如EditPlus或者SublimeText。
    UTF-8与ANSI(即GBK)的互转,可以使用EditPlus工具"文件另存为"或者Encodersoft编码转换工具对.cpp和.h源文件文本进行批量转换.

     

    四、QString显示中文乱码的原因

    我们使用的Windows系统本地字符编码(Local字符集)为GBK。编译器分析出源文件字符编码之后,会进行解码再编码,将源字符集转码成执行字符集。执行字符集一般默认为使用本地字符编码(Local字符集)。

    Qt5可以设置Local字符集,GBK/UTF-8

    QTextCodec *codec = QTextCodec::codecForName("UTF-8");//或者"GBK",不分大小写
    QTextCodec::setCodecForLocale(codec);

    Qt5中QString内部采用unicode字符集,utf-16编码。构造函数QString::QString(const char *str)默认使用fromUtf8(),将str所指的执行字符集从utf-8转码成utf-16。
    由上面fromUtf8()可知,QString需要执行字符集编码为utf-8,然后以utf-8进行解码,再编码为utf-16才能获得正确的字符编码。显示中文乱码的原因其实就是QString转码方式与执行字符集不一致。(比如,源字符集为本地字符集GBK编码,QString以utf-8的方式进行解码,会导致获得错误的二进制编码,再将错误二进制转为utf-16就会出现乱码。)
     

    五、Qt编码指定

    Qt需要在main()函数指定使用的字符编码:

    #include <QTextCodec>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        //设置中文字体  
        a.setFont(QFont("Microsoft Yahei", 9));
    
        //设置中文编码
    #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
    #if _MSC_VER
        QTextCodec *codec = QTextCodec::codecForName("GBK");
    #else
        QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    #endif
        QTextCodec::setCodecForLocale(codec);
        QTextCodec::setCodecForCStrings(codec);
        QTextCodec::setCodecForTr(codec);
    #else
        QTextCodec *codec = QTextCodec::codecForName("UTF-8");
        QTextCodec::setCodecForLocale(codec);
    #endif
    
        return a.exec();
    }

    这里只列举大家最常用的3个编译器(微软VC++的cl编译器,Mingw中的g++,Linux下的g++),源代码分别采用GBK和无BOM的UTF-8以及有BOM的UTF-8这3种编码进行保存,发生的现象如下表所示。

    情况1:指的是Local字符集为GBK

    情况2:指的是Local字符集为UTF-8

    • 源代码的编码

      编译器

      显示正常

      显示乱码

      GBK

      win vs cl

      情况1

      情况2

      win mingw-g++

      情况1

      情况2

      linux g++

      情况1

      情况2

      UTF-8(无BOM)

      win vs cl

      编译失败

      error C2001: 常量中有换行符

      编译失败
      error C2001: 常量中有换行符

      win mingw-g++

      情况2

      情况1

      linux g++

      情况2

      情况1

      UTF-8(有BOM)

      win vs cl

      情况1

      情况2(有#pragma预处理)

      情况2(没有#pragma预处理)

      win mingw-g++

      情况2

      情况1

      linux g++

      情况2

      情况1
    • 如果您使用的是Visual C++编译器,则默认情况下不会将您的源代码视为utf-8编码。除非有BOM,否则它将使用您当前的代码页进行解释。就是说,当使用Visual C++编译程序的时候,它会分析源文件采用何种编码,有BOM标识符则可以正确识别其编码是UTF-8,若没有BOM标识符则认为其使用本地字符集编码(Local字符集)。Local字符集是什么?取决于你的设置QTextCodec *codec = QTextCodec::codecForName(???);
    • 如果源文件是UTF-8+BOM的编码方式,还需要在头文件加入
    #if defined(_MSC_VER) && (_MSC_VER >= 1600)    
    # pragma execution_character_set("utf-8")    
    #endif

    或者添加QMAKE_CXXFLAGS += /utf-8到您的.pro文件中。

    • 如果源文件是UTF-8+无BOM的编码方式,则一定不能加#pragma execution_character_set(“utf-8”),不然会产生乱码。

     

    六、测试案例

    案例1、中文字符串测试

    #if defined(_MSC_VER) && (_MSC_VER >= 1600)    
    # pragma execution_character_set("utf-8")    
    #endif
    
    #include <QApplication>
    #include <QTextCodec>
    #include <QPushButton>
    #include <QDebug>
    #include <QString>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        //设置中文字体  
        a.setFont(QFont("Microsoft Yahei", 9));
    
        //设置中文编码
    #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
    #if _MSC_VER
        QTextCodec *codec = QTextCodec::codecForName("gbk");
    #else
        QTextCodec *codec = QTextCodec::codecForName("utf-8");
    #endif
        QTextCodec::setCodecForLocale(codec);
        QTextCodec::setCodecForCStrings(codec);
        QTextCodec::setCodecForTr(codec);
    #else
        QTextCodec *codec = QTextCodec::codecForName("utf-8");
        QTextCodec::setCodecForLocale(codec);
    #endif
    
        QString str(QObject::tr("1中文"));
        qDebug() << str;
        qDebug() << QStringLiteral("2中文");
        qDebug() << QString::fromLatin1("3中文");
        qDebug() << QString::fromLocal8Bit("4中文");
        qDebug() << QString::fromUtf8("5中文");
        qDebug() << QString::fromWCharArray(L"6中文");
    
        return a.exec();
    }

    当QTextCodec::codecForName("utf-8");时,

    QString::fromLocal8Bit和QString::fromUtf8是等效的。

    当QTextCodec::codecForName("gbk");时,

    QString::fromLocal8Bit和QString::fromUtf8是不等效的。

    案例2、QCom跨平台串口调试助手(http://www.qter.org/?page_id=203)
    源代码qcom\mainwindow.cpp,aboutdialog.cpp等文件用的是UTF-8编码(无BOM);但是qcom\qextserial\*.*文件用的是ANSI编码.在linux环境编译完全OK.
    笔者Windows环境的Qt Creator+微软VC++编译器,环境设置用的是ANSI(即GBK)编码.编译源文件会报错.
    错误提示"fatal error C1018: 意外的 #elif".


    解决方法由两种:

    方法1:

    把qcom\的所有cpp和h文件都用工具转换成ANSI编码,main()函数使用QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));

    方法2:

    先把Qt Creator环境设置用的是UTF-8编码,

    再把qcom\的所有cpp和h文件都用工具转换成UTF-8+BOM编码,请注意,如果文件转换成UTF-8(无BOM),编译仍会失败.main()函数使用QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));//注意,此处仍是"GBK",不是"UTF-8"
    重新编译,OK!

    其它:

     

    七、结论

    Windows环境下,Qt Creator+微软VC++编译器,新建工程,

    1、如果该工程不需要跨平台使用(只在win),那么工程设置请使用GBK的编码方式.

    2、如果该工程要跨平台使用(win+linux),那么工程设置请使用UTF-8+BOM的编码方式.

    另外,还需要在预编译头文件加入

    #if defined(_MSC_VER) && (_MSC_VER >= 1600)    
    # pragma execution_character_set("utf-8")    
    #endif

    或者添加QMAKE_CXXFLAGS += /utf-8到您的.pro文件中。

    3、Linux环境下,Qt Creator+gcc,新建工程,

    没有GBK编码可选,默认是UTF-8(无BOM)编码方式,考虑到跨平台,建议选择UTF-8+BOM的编码方式.

     

    ★★★★★★★★★★★★★★★综上所述,解决乱码的方法概括如下:★★★★★★★★★★★★★★★★★★★★★

    1、如果IDE是Qt Creator,把它的环境设置为“UTF-8+BOM”编码。

    2、如果IDE是Visual Studio,请下载插件,名称是ForceUTF8 (with BOM),所有源文件和头文件都会保存为“UTF-8+BOM”编码。

    3、如果编译器是MSVC,请在预编译头文件加入

    #if defined(_MSC_VER) && (_MSC_VER >= 1600)    
    # pragma execution_character_set("utf-8")    
    #endif

    4、源码文件main函数入口设置中文编码:

        //设置中文编码
    #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
    #if _MSC_VER
        QTextCodec *codec = QTextCodec::codecForName("gbk");
    #else
        QTextCodec *codec = QTextCodec::codecForName("utf-8");
    #endif
        QTextCodec::setCodecForLocale(codec);
        QTextCodec::setCodecForCStrings(codec);
        QTextCodec::setCodecForTr(codec);
    #else
        QTextCodec *codec = QTextCodec::codecForName("utf-8");
        QTextCodec::setCodecForLocale(codec);
    #endif

    5、如此一来,不管是MSVC编译器还是MinGW编译器,都能编译通过,且支持中文!

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

     

    x、参考文献

    Qt官网文档 

    https://wiki.qt.io/Strings_and_encodings_in_Qt

    https://doc.qt.io/qt-5/unicode.html

    ASCII,Unicode和UTF-8完全搞清楚 https://blog.csdn.net/Deft_MKJing/article/details/79460485

    Qt中文乱码原因及解决方案 https://blog.csdn.net/qq_35905572/article/details/95042444

    Qt中文乱码问题 http://blog.csdn.net/brave_heart_lxl/article/details/7186631

    尊重作者,支持原创,如需转载,请附上原地址:https://blog.csdn.net/libaineu2004/article/details/19245205

     

     

    展开全文
  • 信源编码与信道编码

    千次阅读 2014-01-02 22:33:06
    数字电视为何采用信源编码和信道编码? 信源编码主要是解决图片信号的压缩和保存问题,信道编码主要是解决图片信号的传输问题。 信源编码和信道编码都采用的MPEG2技术 采用信源编码可以有效的利用有限的宽带:图像...
  • 拨开字符编码的迷雾--字符编码概述

    万次阅读 2017-11-27 20:46:23
    相信不少人在字符编码上面摔过跟头,这篇文章针对开发中需要了解的字符编码知识进行了简要的讲解,希望能够对大家有所帮助。 1. ASCII及其扩展 1.1 什么是ASCII字符集 字符集就是一系列用于显示的字符的...
  • 语音识别可以直接编码

    万次阅读 2020-04-09 11:25:01
    这样就有了对齐的点,方便编码后进行逐个方波进行对比, 接着对上下两个部分进行N分类,每分类值范围为 (max−m)/N,(m−min)/N (max-m)/N,(m-min)/N (max−m)/N,(m−min)/N 一个值v在某段值范围内就取值为该范围内的...
  • IntelliJ IDEA 统一设置编码为utf-8编码

    万次阅读 多人点赞 2018-06-11 10:49:11
    问题一:File-&gt;Settings-&gt;Editor-&gt;File Encodings问题二:...File Encodings问题三:将项目中的.idea文件夹中的encodings.xml文件中的编码格式改为uft-8问题四:File-&gt;Settings-&...
  • 一篇文章彻底弄懂Base64编码原理

    万次阅读 多人点赞 2018-08-16 07:42:09
    目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一。在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输。那么,...
  • 基于 MATLAB 的 PCM 编码解码实现

    万次阅读 多人点赞 2019-01-12 12:32:53
    文章目录基于 MATLAB 的 PCM 编码解码实现抽样信号PCM 编码PCM 解码失真度分析 基于 MATLAB 的 PCM 编码解码实现 关于 PCM 的原理在此不过多解释,代码使用的是 A 律 13 折线法。 抽样信号 抽样信号的 MATLAB 代码...
  • 大神们,我想问三个问题:第一,视频编码有两种方式(我知道的):帧编码和场编码,帧编码是逐行扫描,场编码是隔行扫描;在图像运动比较剧烈时,最好使用场编码,图像运动相对静止的话,使用帧编码;在进行场编码时会...
  • 霍尔编码器原理及测速--PID—arduino

    千次阅读 2020-03-04 17:13:28
    本人目前是一个大一菜鸟,零基础学的编码器方面,希望我的经验对你有些帮助。 分享一下霍尔编码器电机的使用与测速,我用的是25GA-310直流减速电机带霍尔传感器。先来看一下最基本的 接线方法------- ------S1与S2...
  • emoji图片和编码

    千次下载 热门讨论 2012-05-17 15:11:49
    467个emoji的文件和编码表数据 包括Unicode编码,UTF8编码,UTF16编码,SBUnicode编码
  • ANSI编码和Unicode编码的不同

    万次阅读 热门讨论 2009-10-11 18:06:00
    利用今天一天的时间,研究了一下ANSI编码和Unicode编码的不同,下面把我的研究成果写下来,以备日后参考。ANSI编码最常见的应用就是在Windows当中的记事本程序中,当新建一个记事本,默认的保存编码格式就是ANSI,...
  • NIO中的字符编码问题

    万次阅读 2020-01-05 01:34:45
    获取所有编码 import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.charset.Charset; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class ...
  • 编码:算术编码

    万次阅读 2020-02-10 11:29:53
    算术编码不是简单的将每个信源符号映射成一个码字,而是对整个输入序列分配一个码字,所以平均意义上可以为每个信源符号分配长度小于1的码字。 算术编码操作简单,下面以一个实例讲解算术编码的原理: 设信源有a,b...
  • 层级关系编码code生成

    万次阅读 2019-08-23 16:12:39
    请相信我,你一定会更优秀! 【工具类 No.2】层级关系code生成 备注:开发中经常会遇到一些具备层级关系的匹配; 场景案例: ...1、部门A下有子部门B,B下又有子部门C,C又有子部门D...... 2、分类A下有子分类B,B...
  • 曼彻斯特编码

    万次阅读 2011-08-31 09:22:58
    曼彻斯特编码(Manchester Encoding),也叫做相位编码(PE),是一个同步时钟编码技术,被物理层使用来编码一个同步位流的时钟和数据。曼彻斯特编码被用在以太网媒介系统中。曼彻斯特编码提供一个简单的方式给编码...
  • Android硬编码——音频编码、视频编码及音视频混合

    万次阅读 多人点赞 2017-01-04 15:22:54
    视频编解码对许多Android程序员来说都是Android中比较难的一个知识点。在Android 4.1以前,Android并没有提供硬编硬解的API,所以之前基本上都是采用FFMpeg来做视频软件编解码的,现在FFMpeg在Android的编解码上依旧...
  • IntelliJ IDEA 设置编码为utf-8编码

    万次阅读 多人点赞 2018-06-08 21:58:59
    IntelliJ IDEA 统一设置为utf-8编码问题一:File-&gt;Settings-&gt;Editor-&gt;File Encodings问题二:
  • 1.修改数据库的编码 将数据库(test)的编码方式修改为utf8,如: ALTER DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; 2.修改表的编码 将表(test)的编码方式修改为utf8,
  • 一. Visual Studio字符集 ...但这个设置项不会对编译器处理字符编码产生直接的影响(注意这里的“直接”二字,第3节会说到),只会在工程属性配置属性-->C/C++-->预处理器加入相应的宏: 使用Unicode字符集 --
  • 文本编码

    万次阅读 2016-03-01 00:05:49
    文本编码 文本编码这个问题自三年前就困扰着我,当时是用Python处理多国语言时出现的bug,最后问题解决了,但其中具体逻辑并不懂。后来零零散散接触了不少资料,算是大致弄明白,记录如下。 unicode与ascii等编码...
  • 哈夫曼编码

    千次阅读 2016-05-20 18:25:26
    哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为...
  • 用R语言实现霍夫曼编码

    万次阅读 2020-06-02 10:24:04
    可读性极低,而且其实也没必要用R语言写...mazijuzhen=matrix(,nrow=length(p),ncol=length(p)-1)###码字矩阵:第i行对应向量p的第i个分量所对应的那个待编码消息的编码后的码字 group=matrix(c(1:length(p),rep(NA,le.
  • URL编码

    千次阅读 2015-03-27 20:42:48
    URL编码 我们知道URL指的的是网址,所谓的URL编码指的是对网址上的不安全的字符,例如中文,进行编码编码后的中文方便在网络上传输。 例如,我们提交一个表单,表单内容如下所示:   用户名:李四"/>  提交"/>...
  • 格雷编码

    千次阅读 2016-06-01 12:55:20
    题目描述:格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个二进制的差异。给定一个非负整数 n ,表示该代码中所有二进制的总数,请找出其格雷编码顺序。一个格雷编码顺序必须以 0 开始,并覆盖...
  • 编码总结

    千次阅读 2011-01-23 22:17:00
    1、Linux的vi创建的文档默认编码格式为ANSI 2、Java默认输入中文字的编码格式为unicode 3、MySQL表编码格式如果为“gbk”,那么链接字符串编码必须为“utf-8”
  • 视频压缩编码和音频压缩编码的基本原理

    万次阅读 多人点赞 2014-06-03 00:01:20
    本文介绍一下视频压缩编码和音频压缩编码的基本原理。其实有关视频和音频编码的原理的资料非常的多,但是自己一直也没有去归纳和总结一下,在这里简单总结一下,以作备忘。

空空如也

1 2 3 4 5 ... 20
收藏数 474,980
精华内容 189,992
关键字:

编码