文本的编码方式整理

C#操作文本写入,不同编码方式的结果比对

 

using (FileStream fs = new FileStream("I:\\"+DateTime.Now.ToString("yyyyMMddHHmmssfff")+".txt"
    , FileMode.OpenOrCreate))
{
    using (StreamWriter sWriter = new StreamWriter(fs, Encoding.GetEncoding("GB2312")))
    {
        sWriter.Write("sf002SSJ&^%2£¥€中国筆萬寱巗巘寴寷〕 毕〔畢〕にほんご전지현边〔邊大美女????");
    }
}

 

测试字符串:

sf002SSJ&^%2£¥€中国筆萬寱巗巘寴寷〕 毕〔畢〕にほんご전지현边〔邊大美女????

  

1. 使用特定的编码方式将字符串存入文件

2. 再以这个编码方式打开文件

3. 以16进制输出文件的内容

 

结果记录:

ASCII

StreamWriter sWriter = new StreamWriter(fs, Encoding.ASCII)

 73 66 30 30 32 53 53 4A 26 5E 25 32 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 20 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F

iso-8859-1

StreamWriter sWriter = new StreamWriter(fs, Encoding.GetEncoding("iso-8859-1")

 73 66 30 30 32 53 53 4A 26 5E 25 32 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 20 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F

GB2312

StreamWriter sWriter = new StreamWriter(fs, Encoding.GetEncoding("GB2312"))

 73 66 30 30 32 53 53 4A 26 5E 25 32 A1 EA A3 A4 80 D6 D0 B9 FA B9 50 C8 66 8C 95 8E 73 8E 74 8C 98 8C 9B A1 B3 20 B1 CF A1 B2 AE 85 A1 B3 A4 CB A4 DB A4 F3 A4 B4 3F 3F 3F B1 DF A1 B2 DF 85 B4 F3 C3 C0 C5 AE 3F 3F 3F 3F 3F 3F 3F 3F

GBK

StreamWriter sWriter = new StreamWriter(fs, Encoding.GetEncoding("gbk"))

 73 66 30 30 32 53 53 4A 26 5E 25 32 A1 EA A3 A4 80 D6 D0 B9 FA B9 50 C8 66 8C 95 8E 73 8E 74 8C 98 8C 9B A1 B3 20 B1 CF A1 B2 AE 85 A1 B3 A4 CB A4 DB A4 F3 A4 B4 3F 3F 3F B1 DF A1 B2 DF 85 B4 F3 C3 C0 C5 AE 3F 3F 3F 3F 3F 3F 3F 3F

GB18030

StreamWriter sWriter = new StreamWriter(fs, Encoding.GetEncoding("GB18030"))

 73 66 30 30 32 53 53 4A 26 5E 25 32 A1 EA A3 A4 A2 E3 D6 D0 B9 FA B9 50 C8 66 8C 95 8E 73 8E 74 8C 98 8C 9B A1 B3 20 B1 CF A1 B2 AE 85 A1 B3 A4 CB A4 DB A4 F3 A4 B4 83 33 A8 37 83 33 D5 31 83 36 95 31 B1 DF A1 B2 DF 85 B4 F3 C3 C0 C5 AE 94 32 EE 36 94 32 EE 37 94 32 EE 38 94 32 EF 32

在使用Gb2312时,已经把生僻字,繁体字,日文符号都识别了,大约windows自动使用了GBK字符集

而Gb18030的结果和GBK差异比较大,一个是欧元符号变得更好看了,代码点也变了。其他符号也没有出现乱码也都有解析成功

 

                                                      ( GBK VS GB18030)

Big5

StreamWriter sWriter = new StreamWriter(fs, Encoding.GetEncoding("Big5")

 73 66 30 30 32 53 53 4A 26 5E 25 32 A2 47 A2 44 A3 E1 A4 A4 3F B5 A7 B8 55 EC D8 3F F7 D6 3F 3F A1 66 20 3F A1 65 B2 A6 A1 66 3F 3F 3F 3F 3F 3F 3F 3F A1 65 C3 E4 A4 6A AC FC A4 6B 3F 3F 3F 3F 3F 3F 3F 3F

utf-8 No BOM

StreamWriter sWriter = new StreamWriter(fs)   默认是utf-8 No BOM,等同于new StreamWriter(fs, new UTF8Encoding(false)) 

 73 66 30 30 32 53 53 4A 26 5E 25 32 EF BF A1 EF BF A5 E2 82 AC E4 B8 AD E5 9B BD E7 AD 86 E8 90 AC E5 AF B1 E5 B7 97 E5 B7 98 E5 AF B4 E5 AF B7 E3 80 95 20 E6 AF 95 E3 80 94 E7 95 A2 E3 80 95 E3 81 AB E3 81 BB E3 82 93 E3 81 94 EC A0 84 EC A7 80 ED 98 84 E8 BE B9 E3 80 94 E9 82 8A E5 A4 A7 E7 BE 8E E5 A5 B3 F0 9D 8C 80 F0 9D 8C 81 F0 9D 8C 82 F0 9D 8C 86

utf-8+BOM

StreamWriter sWriter = new StreamWriter(fs,Encoding.UTF8)utf-8+BOM, 等同Encoding.GetEncoding("utf-8")

 EF BB BF 73 66 30 30 32 53 53 4A 26 5E 25 32 EF BF A1 EF BF A5 E2 82 AC E4 B8 AD E5 9B BD E7 AD 86 E8 90 AC E5 AF B1 E5 B7 97 E5 B7 98 E5 AF B4 E5 AF B7 E3 80 95 20 E6 AF 95 E3 80 94 E7 95 A2 E3 80 95 E3 81 AB E3 81 BB E3 82 93 E3 81 94 EC A0 84 EC A7 80 ED 98 84 E8 BE B9 E3 80 94 E9 82 8A E5 A4 A7 E7 BE 8E E5 A5 B3 F0 9D 8C 80 F0 9D 8C 81 F0 9D 8C 82 F0 9D 8C 86

ucs-2

StreamWriter sWriter = new StreamWriter(fs,Encoding.GetEncoding("ucs-2"))   little endian

代码写出的文本,把本来不在ucs-2字符集里的几个字符也识别了,看来就如同前面的gb2312自动换成了GBK一样,这里自动使用了utf-16

通过NotePad++转换格式,得到真正的usc-2 little endian

 FF FE 73 00 66 00 30 00 30 00 32 00 53 00 53 00 4A 00 26 00 5E 00 25 00 32 00 E1 FF E5 FF AC 20 2D 4E FD 56 46 7B 2C 84 F1 5B D7 5D D8 5D F4 5B F7 5B 15 30 20 00 D5 6B 14 30 62 75 15 30 6B 30 7B 30 93 30 54 30 04 C8 C0 C9 04 D6 B9 8F 14 30 8A 90 27 59 8E 7F 73 59 4C 07 30 00 4C 07 70 00 4C 07 B0 00 4C 07

utf-16

StreamWriter sWriter = new StreamWriter(fs,Encoding.GetEncoding("utf-16")     //c#中等效于utf-16le,Encoding.Unicode

FF FE 73 00 66 00 30 00 30 00 32 00 53 00 53 00 4A 00 26 00 5E 00 25 00 32 00 E1 FF E5 FF AC 20 2D 4E FD 56 46 7B 2C 84 F1 5B D7 5D D8 5D F4 5B F7 5B 15 30 20 00 D5 6B 14 30 62 75 15 30 6B 30 7B 30 93 30 54 30 04 C8 C0 C9 04 D6 B9 8F 14 30 8A 90 27 59 8E 7F 73 59 34 D8 00 DF 34 D8 01 DF 34 D8 02 DF 34 D8 06 DF

                                                      ( UCS-2 VS UTF-16)

UCS-2仅仅简单的使用一个16位码元来表示码位,有2^16=65536个码位

UTF-16 是UCS-2的拓展,使用一个或者两个16位的码元来表示码位,包含了一些UCS-2中不存在的字符。

如用于测试的字符串结尾的四个字符,每个字符都使用两个16位来表示,如0xD834 0xDF06(little endian是从低位开始写入数据的,而字节是最小操作单位,一个码元有两个字节,D834写入时先右后左,看上去就变成了34D8了)。这被称作代理对,UCS-2则会把0xD834 0xDF06解释为两个字符。

UTF32

StreamWriter sWriter = new StreamWriter(fs,Encoding.UTF32)    little endian

 FF FE 00 00 73 00 00 00 66 00 00 00 30 00 00 00 30 00 00 00 32 00 00 00 53 00 00 00 53 00 00 00 4A 00 00 00 26 00 00 00 5E 00 00 00 25 00 00 00 32 00 00 00 E1 FF 00 00 E5 FF 00 00 AC 20 00 00 2D 4E 00 00 FD 56 00 00 46 7B 00 00 2C 84 00 00 F1 5B 00 00 D7 5D 00 00 D8 5D 00 00 F4 5B 00 00 F7 5B 00 00 15 30 00 00 20 00 00 00 D5 6B 00 00 14 30 00 00 62 75 00 00 15 30 00 00 6B 30 00 00 7B 30 00 00 93 30 00 00 54 30 00 00 04 C8 00 00 C0 C9 00 00 04 D6 00 00 B9 8F 00 00 14 30 00 00 8A 90 00 00 27 59 00 00 8E 7F 00 00 73 59 00 00 00 D3 01 00 01 D3 01 00 02 D3 01 00 06 D3 01 00

                                                      ( UTF-16 VS UTF-32)

 一个码元代表的字符,UTF-16 与 UTF-32的区别就是UTF-32在高位上补了一个0x0000的字节,而对于两个码元的字符,差异就比较大了。

ucs-2 BigEndian

StreamWriter sWriter = new StreamWriter(fs,Encoding.GetEncoding("ucs-2be"))

 抛出异常:“ucs-2be”不是支持的编码名。有关定义自定义编码的信息,请参阅关于 Encoding.RegisterProvider 方法的文档。

通过NotePad++转换格式得到

 FE FF 00 73 00 66 00 30 00 30 00 32 00 53 00 53 00 4A 00 26 00 5E 00 25 00 32 FF E1 FF E5 20 AC 4E 2D 56 FD 7B 46 84 2C 5B F1 5D D7 5D D8 5B F4 5B F7 30 15 00 20 6B D5 30 14 75 62 30 15 30 6B 30 7B 30 93 30 54 C8 04 C9 C0 D6 04 8F B9 30 14 90 8A 59 27 7F 8E 59 73 07 4C 00 30 07 4C 00 70 07 4C 00 B0 07 4C

utf-16 BigEndian

StreamWriter sWriter = new StreamWriter(fs,Encoding.GetEncoding("utf-16be")) //c#中等效于BigEndianUnicode

 FE FF 00 73 00 66 00 30 00 30 00 32 00 53 00 53 00 4A 00 26 00 5E 00 25 00 32 FF E1 FF E5 20 AC 4E 2D 56 FD 7B 46 84 2C 5B F1 5D D7 5D D8 5B F4 5B F7 30 15 00 20 6B D5 30 14 75 62 30 15 30 6B 30 7B 30 93 30 54 C8 04 C9 C0 D6 04 8F B9 30 14 90 8A 59 27 7F 8E 59 73 D8 34 DF 00 D8 34 DF 01 D8 34 DF 02 D8 34 DF 06

StreamWriter sWriter = new StreamWriter(fs,Encoding.GetEncoding("ucs-4"))

 抛出异常: “ucs-4”不是支持的编码名。有关定义自定义编码的信息,请参阅关于 Encoding.RegisterProvider 方法的文档。

 

utf-32 BigEndian

StreamWriter sWriter = new StreamWriter(fs,Encoding.GetEncoding("utf-32be"))

 00 00 FE FF 00 00 00 73 00 00 00 66 00 00 00 30 00 00 00 30 00 00 00 32 00 00 00 53 00 00 00 53 00 00 00 4A 00 00 00 26 00 00 00 5E 00 00 00 25 00 00 00 32 00 00 FF E1 00 00 FF E5 00 00 20 AC 00 00 4E 2D 00 00 56 FD 00 00 7B 46 00 00 84 2C 00 00 5B F1 00 00 5D D7 00 00 5D D8 00 00 5B F4 00 00 5B F7 00 00 30 15 00 00 00 20 00 00 6B D5 00 00 30 14 00 00 75 62 00 00 30 15 00 00 30 6B 00 00 30 7B 00 00 30 93 00 00 30 54 00 00 C8 04 00 00 C9 C0 00 00 D6 04 00 00 8F B9 00 00 30 14 00 00 90 8A 00 00 59 27 00 00 7F 8E 00 00 59 73 00 01 D3 00 00 01 D3 01 00 01 D3 02 00 01 D3 06

UTF7

StreamWriter sWriter = new StreamWriter(fs,Encoding.UTF7)

Utf-7效果上与base64相似,都是把文本转成了纯由可打印的ascii码字符组成的文本。但utf-7对可打印的ascii码字符是原样输出的

使用Encoding.UTF7解析后的值

————

编码以及与编码关联的代码页 : https://msdn.microsoft.com/zh-cn/library/86hf4sb8(v=vs.80).aspx

 

如果带有BOM,即便选择了错误的解码方式,c#也会解析成功

不带BOM的解析

StreamWriter sWriter = new StreamWriter(fs, new UTF32Encoding(true,false))    utf-32be no BOM

 00 00 00 73 00 00 00 66 00 00 00 30 00 00 00 30 00 00 00 32 00 00 00 53 00 00 00 53 00 00 00 4A 00 00 00 26 00 00 00 5E 00 00 00 25 00 00 00 32 00 00 FF E1 00 00 FF E5 00 00 20 AC 00 00 4E 2D 00 00 56 FD 00 00 7B 46 00 00 84 2C 00 00 5B F1 00 00 5D D7 00 00 5D D8 00 00 5B F4 00 00 5B F7 00 00 30 15 00 00 00 20 00 00 6B D5 00 00 30 14 00 00 75 62 00 00 30 15 00 00 30 6B 00 00 30 7B 00 00 30 93 00 00 30 54 00 00 C8 04 00 00 C9 C0 00 00 D6 04 00 00 8F B9 00 00 30 14 00 00 90 8A 00 00 59 27 00 00 7F 8E 00 00 59 73 00 01 D3 00 00 01 D3 01 00 01 D3 02 00 01 D3 06

只有使用 utf-32be才能解析成功

StreamWriter sWriter = new StreamWriter(fs, new UnicodeEncoding(true,false))

 00 73 00 66 00 30 00 30 00 32 00 53 00 53 00 4A 00 26 00 5E 00 25 00 32 FF E1 FF E5 20 AC 4E 2D 56 FD 7B 46 84 2C 5B F1 5D D7 5D D8 5B F4 5B F7 30 15 00 20 6B D5 30 14 75 62 30 15 30 6B 30 7B 30 93 30 54 C8 04 C9 C0 D6 04 8F B9 30 14 90 8A 59 27 7F 8E 59 73 D8 34 DF 00 D8 34 DF 01 D8 34 DF 02 D8 34 DF 06

只有使用 utf-16be才能解析成功

 

文本编码方式的解析

有BOM存在,则通过BOM可以区分utf-8、utf-16和utf-32,无BOM的情况下,通过预读一段文本内容,一定程度上可以正确解析出utf-8、utf-16和utf-32,它们的存储都很有规律。只要预读的文本内容里有非iso-8859-1字符,就可以确定它是不是utf-8编码,只要有iso-8859-1字符存在就能确定utf-16,只要有单码元的字符存在就能确定utf-32。  其他的文本一般可以直接交给默认字符集。 因为无法进一步区分,地域性的字符集,如gb2312, big5取值范围是重叠的,无法识别。

using System.Text;
using System.IO;

namespace PathMapping.Utils
{
    /// <summary>
    /// 检测文本文件的编码方式
    /// </summary>
    public class EncodeTypeCheck
    {
        private const int MaxLength = 1024;   //最多尝试读取前1kb的内容,进行判别
        private const int BufferSize = 100;
        private int HasReadSize = 0;
        private int RemainSize = 0;
        private int CurUtf8 = 0;
        private int CurUtf16 = 0;
        private byte[] TempBuffer;
        private byte[] Buffer;
        private bool IsUtf8;
        private int CountForUtf32 = 0;

        /// <summary>
        /// 判别文本文件编码方式
        /// </summary>
        /// <param name="path">文件路径</param>
        /// <returns></returns>
        public Encoding GetFileEncodeType(string path)
        {
            using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader br = new BinaryReader(fs))
                {
                    //通过BOM判别,过滤掉utf-32+BOM,utf-16+BOM 和 utf-8+BOM,ucs-2,也按utf-16处理
                    byte[] buffer = br.ReadBytes(4);
                    if (buffer.Length > 1)
                    {
                        if (buffer[0] >= 0xEF)
                        {
                            if (buffer[0] == 0xEF && buffer[1] == 0xBB)
                            {
                                if (buffer.Length > 2 && buffer[2] == 0xBF)
                                {
                                    return Encoding.UTF8;
                                }
                            }
                            else if (buffer[0] == 0xFF && buffer[1] == 0xFE)
                            {
                                if(buffer.Length > 3 && buffer[2] == 0x00 && buffer[3] == 0x00)
                                {
                                    return Encoding.UTF32;
                                }
                                else
                                {
                                    return Encoding.Unicode;
                                }
                            }
                            else if (buffer[0] == 0xFE && buffer[1] == 0xFF)
                            {
                                return Encoding.BigEndianUnicode;
                            }
                        }
                        else if (buffer[0] == 0x00 && buffer.Length > 3)
                        {
                            if (buffer[1] == 0x00 && buffer[2] == 0xFE && buffer[3] == 0xFF)
                            {
                                return Encoding.GetEncoding("utf-32be");
                            }
                        }

                        //无法通过BOM识别,逐个字节扫描文件来判别
                        fs.Seek(0, SeekOrigin.Begin);
                        return CheckEncodeType(br);
                    }

                    return Encoding.GetEncoding("GB18030");
                }
            }
        }

        private  Encoding CheckEncodeType(BinaryReader br)
        {
            TempBuffer = new byte[BufferSize + 10];
            HasReadSize = 0;
            RemainSize = 0;
            CurUtf8 = 0;
            CurUtf16 = 0;
            IsUtf8 = true;      //假设为utf-8
            CountForUtf32 = 0;  //统计是第几个码元

            while (HasReadSize < MaxLength && (Buffer = br.ReadBytes(BufferSize)).Length > 0)
            {
                CopyBytes();

                while (CurUtf8 + 5 < RemainSize && CurUtf16 + 1 < RemainSize)
                {
                    CheckUtf8();

                    while (CurUtf16 + 1 < RemainSize)
                    {
                        CountForUtf32++;

                        byte head = TempBuffer[CurUtf16];
                        byte tail = TempBuffer[CurUtf16 + 1];
                        if (head == 0x00)
                        {
                            if (tail == 0x00)
                            {
                                if (CountForUtf32 % 2 == 1)
                                    return Encoding.GetEncoding("utf-32be");
                                else
                                    return Encoding.GetEncoding("utf-32le");
                            }
                            else
                            {
                                return Encoding.GetEncoding("utf-16be");
                            }
                        }
                        else
                        {
                            if (tail == 0x00)
                            {
                                return Encoding.GetEncoding("utf-16");
                            }
                            CurUtf16 = CurUtf16 + 2;
                        }
                    }
                }
            }

            if (IsUtf8)
            {
                return Encoding.UTF8;
            }
            return Encoding.GetEncoding("GB18030");
        }

        private void CheckUtf8()
        {
            while (CurUtf8 + 5 < RemainSize && IsUtf8)
            {
                byte head = TempBuffer[CurUtf8];
                if (head >= 0xE0 && head <= 0xEF)//是3个字节的格式
                {
                    CheckUtf8(TempBuffer[CurUtf8 + 1], IsUtf8);
                    CheckUtf8(TempBuffer[CurUtf8 + 2], IsUtf8);
                    CurUtf8 = CurUtf8 + 3;
                }
                else if (head >= 0xC0 && head <= 0xDF)//是2个字节的格式
                {
                    CheckUtf8(TempBuffer[CurUtf8 + 1], IsUtf8);
                    CurUtf8 = CurUtf8 + 2;
                }
                else if (head >= 0 && head <= 0x7F)//是1个字节的格式
                {
                    CurUtf8++;
                }
                else if (head >= 0xF0 && head <= 0xF7)//是4个字节的格式
                {
                    CheckUtf8(TempBuffer[CurUtf8 + 1], IsUtf8);
                    CheckUtf8(TempBuffer[CurUtf8 + 2], IsUtf8);
                    CheckUtf8(TempBuffer[CurUtf8 + 3], IsUtf8);
                    CurUtf8 = CurUtf8 + 4;
                }
                else if (head >= 0xF8 && head <= 0xFB)//是5个字节的格式
                {
                    CheckUtf8(TempBuffer[CurUtf8 + 1], IsUtf8);
                    CheckUtf8(TempBuffer[CurUtf8 + 2], IsUtf8);
                    CheckUtf8(TempBuffer[CurUtf8 + 3], IsUtf8);
                    CheckUtf8(TempBuffer[CurUtf8 + 4], IsUtf8);
                    CurUtf8 = CurUtf8 + 5;
                }
                else
                {
                    IsUtf8 = false;  //不是Utf8
                }
            }
            if (!IsUtf8)
            {
                CurUtf8 = RemainSize;
            }
        }
        private void CheckUtf8(byte x, bool utf8)
        {
            if (x < 0x80 || x > 0x8f)
            {
                utf8 = false;    //不是Utf8
            }
        }

        private void CopyBytes()
        {
            if (CurUtf8 >= CurUtf16 && CurUtf16 > 0)
            {
                for (int i = CurUtf16, j = 0; i < RemainSize; i++, j++)
                {
                    TempBuffer[j] = TempBuffer[i];
                }
                RemainSize = RemainSize - CurUtf16;
                CurUtf8 = CurUtf8 - CurUtf16;
                CurUtf16 = 0;
            }
            else if (CurUtf16 > CurUtf8 && CurUtf8 > 0)
            {
                for (int i = CurUtf8, j = 0; i < RemainSize; i++, j++)
                {
                    TempBuffer[j] = TempBuffer[i];
                }
                RemainSize = RemainSize - CurUtf8;
                CurUtf16 = CurUtf16 - CurUtf8;
                CurUtf8 = 0;
            }

            int length = Buffer.Length;
            HasReadSize += length;
            for (int i = 0; i < length; i++, RemainSize++)
            {
                TempBuffer[RemainSize] = Buffer[i];
            }
        }
    }
}

 

 

————

概念

字符集、代码点、代码单元

字符集就是字符的集合,为了用计算机表示这些字符,为字符集中的每个字符都分配一个“代码点”,每个代码点都是一个特定的唯一数值。

在每种编码形式中,代码点被映射到一个或多个代码单元。“代码单元”是各个编码方式中的单个单元。代码单元的大小等效于特定编码方式的位数。

常用字符集

ASCII及其扩展字符集

作用:表语英语及西欧语言。
位数:ASCII是用7位表示的,能表示128个字符。扩展ASCII则使用8位,能表示256个字符。
范围:ASCII从00到7F,扩展从00到FF,但扩展ASCII不再是国际标准。

标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。

  其中:0~31及127(共33个)是控制字符或通信专用字符,属于不可显示字符,文本输出时都要使用转义符号。

在标准ASCII中,其最高位(b7)用作奇偶校验位。

许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。

ISO-8859-1字符集

作用:扩展ASCII,表示西欧、希腊语等。
位数:8位,表示256个字符
范围:00-FF,00-7F之间完全和ASCII一致,80-9F之间是控制字符,A0-FF之间是文字符号。

ISO-8859-1收录的字符除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号出现的比较晚,没有被收录在ISO-8859-1当中。
因为ISO-8859-1编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。这是个很重要的特性,MySQL数据库默认编码是Latin1就是利用了这个特性(Latin1是ISO-8859-1的别名)。

BIG5字符集

作用:统一繁体字编码,兼容ASCII。
位数:使用2个字节表示,表示13461 个汉字和符号。
范围:高字节从A1到F9,低字节从40到7E,A1到FE。因此,其第一字节的最高位是1,第二字节的最高位则可能是1,也可能是0。

Big-5 是通行于台湾、香港地区的一个繁体字编码方案。

  1. 符号408个,编码位置为A140~A3FE(实际止于A3BF,末尾有空白位 置)。
  2. 汉字13053个,分为常用字和次常用字两部分,各部分中的汉字按笔划/部首排列。其中:
    1. 常用字5401个,编码位置为A440~C67E。包括台湾教育部颁布的《常用国字标准字体表》中的全部汉字4808个,台湾国中、国小教科书常用字587个,异体字6个。 
    2. 次常用字7652个,编码位置为C940~F9FE(实际止于F9D5,末尾有空白位置)。包括台湾教育部次常用国字标准字体表》的全部汉字6341个,《罕用国字标准字体表》中使用频率较高的字 1311 个。
  3. 其余的A040~A0FE、C6A1~C8FE、FA40~FEFE 为空白区域。一些空白位置,经常被用于用户造字区,而且多存放香港常用字和粤语方言字。 
     现在流行的BIG-5 码字库,在F9D6~F9DC 位置大都有7个常用字,据说为倚天系统所增。若计此7 字,则全数为13060个汉字,13468 个汉字和符号。此外,一些BIG-5 码字库,如Windows 繁体中文版的True Type 细明体(华康科技提供,2.0 版),在 F9DD~F9FE 位置还有33 个制表符和1个“  ■”符号。

GB2312字符集

作用:国家简体中文字符集,兼容ASCII。
位数:使用2个字节表示,能表示7445个符号,包括6763个汉字,几乎覆盖所有高频率汉字。
范围:高字节从A1到F7, 低字节从A1到FE。将高字节和低字节分别加上0XA0即可得到编码。

GB2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时,GB2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

GB2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。
  01-09区为特殊符号。
  16-55区为一级汉字,按拼音排序。
  56-87区为二级汉字,按部首/笔画排序。
  10-15区及88-94区则未有编码。
 
字节结构
  在使用GB2312的程序中,通常采用EUC储存方法,以便兼容于ASCII。浏览器编码表上的“GB2312”,通常都是指“EUC-CN”表示法。
  每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节)”,第二个字节称为“低位字节”(也称“位字节”)。
  “高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。
  例如“啊”字在大多数程序中,会以两个字节,0xB0(第一个字节) 0xA1(第二个字节)储存。区位码=区字节+位字节(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。
 

GBK字符集

作用:它是GB2312的扩展,支持ISO/IEC10646—1和GB 13000—1的全部全部中、日、韩(CJK)汉字,共计20902字,兼容GB2312。
位数:使用2个字节表示,可表示21886个字符。
范围:高字节从81到FE,低字节从40到FE。

GBK自身并非国家标准,只是曾由国家技术监督局标准化司、电子工业部科技与质量监督司公布为“技术规范指导性文件”。后续国家标准GB18030技术上兼容GBK

GB18030字符集

全称:国家标准GB 18030-2005《信息技术 中文编码字符集》

作用:它解决了中文、日文、朝鲜语等的编码,兼容GBK。
位数:它采用变字节表示(1 ASCII,2,4字节)。
范围:1字节从00到7F; 2字节高字节从81到FE,低字节从40到7E和80到FE;4字节第一三字节从81到FE,第二四字节从30到39。

与GB 2312-1980完全兼容,与GBK基本兼容,支持GB 13000及Unicode的全部统一汉字,共收录汉字70244个。

字节结构

单字节,其值从0到0x7F。

双字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x40到0xFE(不包括0x7F)。

四字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x30到0x39,第三个字节从0x81到0xFE,第四个字节从0x30到0x39。

 

版本

GB 18030-2000,兼容 Unicode 3.0 中日韩统一表意文字,共收27533个汉字;2000年3月17日发布

GB 18030-2005,更新至 Unicode 3.1 中日韩统一表意文字及增加少数民族文字,共有70244个汉字;2005年11月8日发布、2006年5月1日实施

 

UCS字符集

作用:国际标准 ISO 10646 定义了通用字符集 (Universal Character Set)。它是与UNICODE同类的组织。
位数:它有UCS-2和UCS-4两种格式,分别是2字节和4字节。
范围:目前,UCS-4只是在UCS-2前面加了0×0000。

UNICODE字符集

作用:为世界650种语言进行统一编码,兼容ISO-8859-1。
位数:UNICODE字符集有多个编码方式,分别是UTF-8,UTF-16,UTF-32和 UTF-7。

 

unicode和ucs及其编码方式

     国际标准化组织(ISO)和多语言软件制造商组成的统一码联盟各自尝试创立单一字符集。前者开发的 ISO/IEC 10646 项目,后者开发的统一码项目。因此最初制定了不同的标准。之后经过双方的协作,从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都存在,并独立地公布各自的标准。但统一码联盟和ISO/IEC JTC1/SC2都同意保持两者标准的码表兼容,并紧密地共同调整任何未来的扩展。在发布的时候,Unicode一般都会采用有关字码最常见的字型,但ISO 10646一般都尽可能采用Century字型。

     unicode的编码可以和UCS-2和UCS-4保持一致。但是又略有不同。UTF-16是UCS-2的扩展,UTF-32是UCS-4的子集。也就是说,UTF-16的实现上对code point的支持范围超过UCS-2,而UTF-32对code point的表示却又在UCS-4的范围之内。

     在编码层面上,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。
     UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。
     UCS-4根据最高位为0的最高字节分成2^7=128个组(group)。每个group再根据次高字节分为256个平面(plane)。每个plane根据第3个字节分为256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。
     group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。
     将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。

     Unicode最初支持16位的code point,后来发现不够用,于是用UTF-16扩展UCS-2。在BMP区域内的一片连续空间(U+D800~U+DFFF)的码位区段是永久保留不映射到字符,因此UTF-16利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。具体算法参考wiki:utf-16 。


      所以,utf-16能表示的范围最大能到U+10FFFF,包含1个基本平面(BMP)和16个辅助平面。理论上UCS-4编码范围能达到U+7FFFFFFF,但是因为unicode和iso达成共识,只会用17个平面内的字符,所以UTF-32是UCS-4的子集。但是UTF-16是定长的编码,和UCS-4无论实现和编码都是基本一样的。

UTF-8 

UTF-8(8-bit Unicode Transformation Format)是一种可变长度字符编码,又称万国码。由Ken Thompson于1992年创建。用1到6个字节编码UNICODE字符。用在网页上可以同一页面显示中文简体繁体及其它语言。

编码方式如下:

 

Unicode/UCS-4
bit数
UTF-8
byte数
备注
0000 ~ 007F
0~7
0XXX XXXX
1
与ASCII码一致
  
0080 ~ 07FF
8~11
110X XXXX
10XX XXXX
2

  
0800 ~FFFF
12~16
1110XXXX
10XX XXXX
10XX XXXX
3
基本定义范围:0~FFFF
1 0000 ~1F FFFF
17~21
1111 0XXX
10XX XXXX
10XX XXXX
10XX XXXX
4
Unicode6.1定义范围:0~10 FFFF
20 0000 ~3FF FFFF
22~26
1111 10XX
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
5
说明:此非unicode编码范围,属于UCS-4 编码
早期的规范UTF-8可以到达6字节序列,可以覆盖到31位元(通用字符集原来的极限)。尽管如此,2003年11月UTF-8 被 RFC 3629 重新规范,只能使用原来Unicode定义的区域, U+0000到U+10FFFF。根据规范,这些字节值将无法出现在合法 UTF-8序列中
400 0000 ~7FFF FFFF
27~31
1111 110X
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
6

 

例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定 要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001,用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

UTF-8编码的第一个字节打头的1的个数标明了字节总数。

汉字是3个字节,所以UTF-8编码的文件比GB2312大。url编码是在字节前加%。所以UTF-8编码的汉字url编码后几乎全是%E打头。

第一个字节最多6个1打头,也就是不能取,FE,FF,

后面的字节同样至少要1个1打头,且不会取FE,FF,(1-6个1)

 

UTF-16

UTF-16,代码单元采用两个字节,两个字节是16位,故叫UTF-16。UTF-16表示字符很方便,简化了字符串操作, 因此Java以UTF-16作为内存的字符存储格式
UTF-32:代码单元采用4字节。
 

UTF-8、UTF-16和UTF-32比较

UTF-8、UTF-16和UTF-32都可以表示有效编码空间 (U+000000-U+10FFFF) 内的所有Unicode字符。
使用UTF-8编码时ASCII字符只占1个字节,存储效率比较高,适用于拉丁字符较多的场合以节省空间。
对于大多数非拉丁字符(如中文和日文)来说,UTF-16所需存储空间最小,每个字符只占2个字节。适用于内存和磁盘中操作字符使用。
采用UTF-16和UTF-32会有Big Endian和Little Endian之分,而UTF-8则没有字节顺序问题,所以UTF-8适合传输和通信。
UTF-32采用4字节编码,一方面处理速度比较快,但另一方面也浪费了大量空间,影响传输速度,因而很少使用。

UTF的字节序和BOM

UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释 一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我 们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?

Unicode规范中推荐的标记字节顺序的方法是BOM。BOM是Byte order Mark。

在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输 字符"ZERO WIDTH NO-BREAK SPACE"。

这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。

UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

 

因为bom头有时会对数据造成影响,所以有时需要主动控制写入生成no bom的文件,或以忽略BOM头的方式读取文件。C#通过UTF8Encoding(false)操作就可以了。

Windows就是使用BOM来标记文本文件的编码方式的,在windows下文本转码另存在时,默认都会加BOM,但并不是所有操作系统的支持这一特性,文件的Bom是可能不存在的。

 

 
 

utf-7

utf-7同样是一种可变长度字元编码方式,用以将 Unicode 字元以 ASCII 编码的字元串来呈现,可以应用在电子邮件传输之类的应用。

SMTP [1]  为基本的电子邮件传输标准之一,其指明了传输格式为 US-ASCII ,并且不允许超过 ASCII 所定义的字元范围以外的位元值,也就是说八位元的字串将无法正常的被传输。在这一背景下,提出了utf-7。现今的邮件的传输方式由于都已支持 UTF-8,UTF-7 则已走入历史而很少再被使用。

如何判断字符集编码

UNICODE,根据前几个字节可以判断UNICODE字符集的各种编码,叫做Byte Order Mask方法BOM:
UTF-8: EFBBBF (符合UTF-8格式,请看上面。但没有含义在UCS即UNICODE中)
UTF-16 Big Endian:FEFF (没有含义在UCS-2中)
UTF-16 Little Endian:FFFE (没有含义在UCS-2中)
UTF-32 Big Endian:0000FEFF (没有含义在UCS-4中)
UTF-32 Little Endian:FFFE0000 (没有含义在UCS-4中)
GB2312:高字节和低字节的第1位都是1。
BIG5,GBK&GB18030:高字节的第1位为1。操作系统有默认的编码,常为GBK。通过判断高字节的第1位从而知道是ASCII或者汉字编码。

 

附:

EUC

EUC全名为Extended Unix Code,是一个使用8位编码来表示字符的方法。EUC最初是针对Unix系统,由一些Unix公司所开发,于1991年标准化。EUC基于ISO/IEC 2022的7位编码标准,因此单字节的编码空间为94,双字节的编码空间(区位码)为94x94。把每个区位加上0xA0来表示,以便符合ISO 2022。它主要用于表示及储存汉语文字、日语文字及朝鲜文字。

EUC定义了4个单独的码集(code set)。码集0总是对应于7位的ASCII(或其它的各国定义的ISO 646),包括了ISO 2022定义的C0与G0空间的值。码集1, 2, 3表示G1空间的值。其中,码集1表示一些未经修饰(unadorned)的字符。码集2的字符编码以0x8E(属于C1控制字符,或称SS2)为第一字节。码集3的字符编码以0x8F(另一个属于C1的控制字符,或称SS3)为第一字节。码集0总是编码为单字节;码集2、3总是编码为至少2个字节;码集1编码为1-3个字节。

 

EUC-CN

EUC-CN是GB 2312最常用的表示方法。浏览器编码表上的“GB2312”,通常都是指“EUC-CN”表示法。

ASCII字符,范围为0x21-0x7E,直接用单字节表示。这是码集0。

GB 2312字元使用两个字节来表示。这是码集1。

“第一位字节”使用0xA1-0xF7“,第二位字节”使用0xA1-0xFE。

GB2312没有使用码集2、码集3部分。

举例来说,“啊”字是GB 2312之中的第一个汉字,它的区位码是1601。在EUC-CN之中,它把0xA0+16=0xB0,0xA0+1=0xA1,得出0xB0A1。

 

EUC-JP

EUC-JP用来储存日本JIS X 0208(旧称JIS C 6226)及JIS X 0212字集的字符,主要影响了类Unix操作系统的日文表示与处理。但是,日文Windows操作系统较多使用ISO-2022-JP或Shift JIS的方法来表示。

ASCII字符,范围为0x21-0x7E,直接用单字节表示。这是码集0。

半角片假名使用两个字节来表示。这是码集2。

“第一位字节”使用0x8E“,第二位字节”使用0xA1-0xDF。

JIS X 0208字元使用两个字节来表示。这是码集1。

“第一位字节”使用0xA1-0xFE,“第二位字节”使用0xA1-0xFE。

JIS X 0212字元使用三个字节来表示。这是码集3。

“第一位字节”使用0x8F,“第二位字节”使用0xA1-0xFE,“第三位字节”使用0xA1-0xFE。

 

EUC-JISX0213

EUC-JISX0213是一个制定中的EUC规格,用来表示JIS X 0213字集的字符。

半角片假名使用两个字节来表示。

“第一位字节”使用0x8E“第二位字节”使用0xA1-0xDF

JIS X 0213第一字面字元使用两个字节来表示。

“第一位字节”使用0xA1-0xFE“第二位字节”使用0xA1-0xFE

JIS X 0213第二字面字元使用三个字节来表示。

“第一位字节”使用0x8F“第二位字节”使用0xA1-0xFE“第三位字节”使用0xA1-0xFE

 

EUC-KR

EUC-KR用来储存韩国KS X 1001字集(旧称KS C 5601)的字符。此规格由KS X 2901(旧称KS C 5861)定义。

KS X 1001字元使用两个字节来表示。

“高位字节”使用0xA1-0xFE“低位字节”使用0xA1-0xFE

 

EUC-TW

EUC-TW为台湾使用的汉字编码方法之一,以CNS 11643字表为基础;但是台湾普遍使用大五码,EUC-TW甚少使用。

CNS 11643第一字面的字元使用两个字节来表示。

“第一位字节”使用0xA1-0xFE“第二位字节”使用0xA1-0xFE

CNS 11643其他字面的字元使用四个字节来表示。

“第一位字节”使用0x8E“第二位字节”使用0xA1-0xB0(0xA1-0xA7分别代表第1至第7个字面,其余未定义)“第三位字节”使用0xA1-0xFE“第四位字节”使用0xA1-0xFE

(CNS 11643第一字面可选择使用两个字节或四个字节来表示)

中日韩统一表意文字(CJK/Unihan)

中日韩统一表意文字(英语:CJK Unified Ideographs),也称统一汉字(英语:Unihan),目的是要把分别来自中文、日文、韩文、越南文、壮文中,起源相同、本义相同、形状一样或稍异的表意文字,赋予其在ISO 10646及万国码标准中相同编码。

所谓“起源相同、本义相同、形状一样或稍异的表意文字”,主要为汉字,包括繁体字、简体字、日本汉字(漢字/かんじ)、韩国汉字(漢字/한자)、越南的喃字(?喃/Chữ Nôm)与儒字(?儒/Chữ Nho)、方块壮字。

此计划原本只包含中文、日文及韩文中所使用的汉字,旧称中日韩(CJK)统一表意文字(Unified Ideographs)。后来,此计划加入了越南文的喃字,所以合称中日韩越(CJKV)统一表意文字。

1993年5月,正式制订了最初的中日韩统一表意文字,位于U+4E00–U+9FFF这个区域,共20,902个字。一个月后,制订了统一码1.1。

1999年,依据ISO/IEC 10646的第17个修正案(Amendment 17)订定了扩充区A,于U+3400–U+4DFF加入了6,582个字。

2001年,依据ISO/IEC 10646-2,新增了扩充区B,有42,711字。位于U+20000–U+2A6FF。但因在短时间内增加了大量的汉字,导致产生了许多重复的字形。

2005年,依据ISO/IEC 10646:2003的第1个修正案(Amendment 1),基本多文种平面增加了U+9FA6到U+9FBB等22个汉字。

2009年,统一码5.2扩充区C增加了U+2A700-U+2B734和U+9FC4~U+9FCB。

2010年,统一码6.0扩充区D增加了U+2B740-U+2B81F。

2012年, 1字增加U+9FCC。

 
 

汉字对应表

unicode.org,或者专门的汉字对应表

转载于:https://www.cnblogs.com/wj033/p/3462553.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值