字符编码

这些是什么?

基础概念

字符集和字符编码

  • ASCII: 这是最基础的字符编码,简单理解可以认为是包含了键盘上的所有字符
  • GB_XXXX: 单就我们熟悉的中文,ASCII字符编码一定是不够的,由此GB定义了字符集以及编码方式覆盖了中文文字(也有日韩、emoji等)
  • Unicode: 国际通用的字符集编码,覆盖了世界上大部分的文字系统
  • UTF-8: 看起来Unicode是最好的解决方式了,那么UTF-8出现和很广泛的使用又是为了什么呢?
    • Unicode:是一个符号集,规定了各种文字符号的代码,为了覆盖这么多文字,Unicode的占用字节数很大,比如ASCII的任意字符,在ASCII里面表示只需要1字节,但是一些中文在Unicode里面需要4字节;Unicode为了统一就把只需要1字节的前面3个字节补0存放;结果就是在计算机资源(存储、带宽)匮乏的年代,Unicode是一种很奢侈的编码方式
    • UTF-8基于一定的规则,把Unicode从定长编码成变长的,可以使用1-4字节表示一个符号,根据不同的符号变化字节的长度,这样就节省很多的资源占用
    • UTF-8的规则
      • 在ASCII码的范围,用一个字节表示,超出ASCII码的范围就用字节表示,这就形成了我们上面看到的UTF-8的表示方法,这様的好处是当UNICODE文件中只有ASCII码时,存储的文件都为一个字节,所以就是普通的ASCII文件无异,读取的时候也是如此,所以能与以前的ASCII文件兼容。
      • 大于ASCII码的,就会由上面的第一字节的前几位表示该unicode字符的长度,比如110xxxxx前三位的二进制表示告诉我们这是个2BYTE的UNICODE字符;1110xxxx是个三位的UNICODE字符,依此类推;xxx的位置由字符编码数的二进制表示的位填入。越靠右的x具有越少的特殊意义。只用最短的那个足够表达一个字符编码数的多字节串。注意在多字节串中,第一个字节的开头”1”的数目就是整个串中字节的数目。
  • GB_XXXX 和 UTF-8: 这里其实更合适的讨论是GB_XXXX 和 Unicode:一个汉字在GB_XXXX编码的值和Unicode里的值是不一样的,而UTF-8只是把Unicode用变长的方式编码出来;所以如果需要从GB_XXXX编码转换到UTF-8是2步操作:1-通过映射表转换到Unicode,2-Unicode用UTF-8做编码

如何区分编码

  • 我们写入一个文件 这是一段中文
    GBK
    UTF-8
    UTF-8 BOM
    UTF-16 BE
    UTF-16 LE

  • hex 值可以看出,UTF 相关的编码前N个字节,是有一定的规则的 (https://unicode.org/faq/utf_bom.html)

    Bytes Encoding Form
    00 00 FE FF UTF-32, big-endian
    FF FE 00 00 UTF-32, little-endian
    FE FF UTF-16, big-endian
    FF FE UTF-16, little-endian
    EF BB BF UTF-8
  • 既然 UTF 编码可以通过前面的字节判断的,那么其他就是GBK编码了(这只是一个简单的编码猜测方式)

  • 做文件GBK-UTF8转换的脚本(自行判断文件编码,并做转换,基于iconv)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

if [ ! $# == 2 ]; then
echo "usage: $0 path suffix filter....";
exit;
fi

function isGbk()
{
local temp=`iconv -f gbk $1 1>/dev/null 2>/dev/null && echo 'true'`;
if [ "$temp" == 'true' ]; then
return 0;
fi;
return -1;
}

echo "find $1 -type f -name \"$2\" ....";
find $1 -type f -name "*$2"|while read line;do
$(isGbk $line)
if [ $? = '0' ]; then
echo $line;
iconv -f gbk -t UTF-8 $line > ${line}.utf8;
mv ${line}.utf8 $line;
fi;
done

  • BE: big endian 地址端存放位字节
  • LE: little endian 地址端存放位字节

常用编码检查

  • shell: file aaa.txt
  • IDE: txt、vscode (上图用的就是vscode)

默认编码

  • windows: GBK (中文)
  • linux: UTF-8
  • IDE: VSCode: UTF-8
  • 语言: C/C++ 处理的是二进制数据,所以本身是没有默认编码,而编码是跟源文件(代码文件、读取的磁盘文件)

区域设置

  • 既然语言如 C/C++ 处理的是二进制数据(字符集本身是二进制数据的拼合),又有那么多编码并且各个编码所占字节又不同的情况下,我们需要告知程序在做字符转换的时候用那种拆分方式拆分一个字符串数据.
    setlocale
    告知程序在做多字符-宽字符 mbstowcs, wcstombs 互相转换的时候如何解码字符 (需要注意的是这里linux、windows设置是不一样的)
    linux: (https://man7.org/linux/man-pages/man7/locale.7.html)
    windows: (https://docs.microsoft.com/en-us/cpp/c-runtime-library/country-region-strings?view=msvc-160)

  • 如果你不需要做字符转换(UTF-8 是用途最广的编码方式),只需要保证程序读取的文件都是相同编码(UTF-8)的,如果你的程序里有(比如:中文这种),也需要保证你的源文件是(UTF-8)编码的

未完

C++11

备忘

** MySQL 的”utf8mb4”才是真正的”UTF-8” **
https://zhuanlan.zhihu.com/p/63360270

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
drop table if exists unioncode_test;

create table unioncode_test(
`id` int primary key auto_increment,
`name` varchar(100) default null
) default charset=utf8;

insert into unioncode_test (`name`) values('𠖲');

select * from unioncode_test;

ALTER TABLE unioncode_test CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

insert into unioncode_test (`name`) values('𠖲');

select * from unioncode_test;

mysql 结果

------ 本文结束 ------
------ 版权声明:转载请注明出处 ------