이 글은 Windows 메모장의 선택적 문자 인코딩에 대한 자세한 소개를 제공합니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.
Windows 메모장(Notepad)에서 파일을 저장하기 위한 인코딩 옵션은 무엇을 의미합니까...
이 문서에서는 Windows 메모장의 동작을 간단히 테스트합니다.
Windows 메모장 인코딩에는 ANSI, 유니코드, 유니코드 빅 엔디안 및 UTF-8이 포함됩니다.
이 글은 널리 사용되는 소프트웨어의 기술적 사실만을 설명할 뿐, 작성자가 해당 소프트웨어의 사용을 지지하거나 반대한다는 의미는 아닙니다.
사실 저자는 컴퓨터 프로그램 코드 작업을 위해 어떤 경우에도 Windows 메모장을 사용하지 말 것을 권장합니다.
이 문서는 64비트 Windows 7 중국어 간체 버전의 특정 인스턴스에서만 확인되었으며 참조용입니다. 동일하거나 다른 시스템에서 일관된 결과가 재현될 수 있다는 보장은 없습니다.
이 문서에서는 유니코드의 인코딩과 바이트 직렬화를 엄격하게 구분합니다.
유니코드의 인코딩은 단순히 숫자(보통 16진수로 작성됨)를 사용하여 문자를 일대일로 표현하는 작업을 의미합니다. 이 숫자의 범위는 유니코드 표준에 의해서만 제한되며 컴퓨터와는 아무 관련이 없습니다.
유니코드의 바이트 직렬화는 유니코드 표준 범위 내의 숫자를 컴퓨터 메모리에 쓸 수 있도록 N바이트로 나타내는 작업을 말합니다.
테스트 케이스는 "锟斤拷[줄바꿈]a[줄바꿈]"입니다. (锟斤拷는 신념입니다.)
모든 문자의 GBK 및 유니코드 인코딩은 다음과 같습니다:
锟GBK=EFBF
Unicode=U+951F
EFBF
Unicode=U+951F
斤 GBK=BDEF
Unicode=U+65A4
拷 GBK=BFBD
Unicode=U+62F7
以下ASCII字符的GBK和Unicode编码与ASCII一致:
a=0x61
CR=0x0D
LF=0x0A
(Windows一个换行符占有两个字符:CR+LF)
在简体中文系统下,ANSI就是中华人民共和国国家标准定义的GBK编码。
Windows Notepad使用ANSI存储这个文件的结果如下:
EF BF BD EF BF BD 0D 0A 61 0D 0A ----- ----- ----- -- -- -- -- --
简单的使用GBK编码存储了所有的字符。最高位不是1的单字节并等同于ASCII,否则双字节。
这里要注意字节序(Endian)的问题[注A]
。可以看到这里的字节序是大端在先(big-endian)的。
但是不必特意强调“大端在先的GBK”——因为从GB2312开始,标准就规定了存储方式是大端在先的[注B]
。后来的GBK和GB18030-2000向下兼容。
ANSI的麻烦就是依赖系统——其他语言系统的ANSI就不是GBK了,打开GBK的文件必然乱码。并且GBK的字符集本身也太小。
(千万不要说“我只用中文”——少了Unicode那些符号,网上那些颜文字都打不出来)
Windows Notepad所说的“Unicode”、“Unicode big endian”和UTF-8,全都是同样的Unicode编码的不同的字节序列化存储方法。
这里的Unicode指UTF-16[注C]
。UTF-16是极其简单粗暴的序列化方法——绝大多数的Unicode字符都在U+0000~U+FFFF的范围内[注D]
,那就每个字符用两个字节,把Unicode编码的原始值写盘。
注意ASCII字符也必须浪费一倍的空间存储高8位的0x00——因为如果把高8位的0略了,解析时就再也没有其他的依据去断字。
对于UTF-16就存在大端和小端的问题了——UTF-16并不规定字节的大端在前还是小端在前。但UTF-16并不包含表示字节序的信息,总不能人工看看哪个解析是不乱码的吧……
Unicode提供的解决方式是,把一个零宽无断字空格符(U+FEFF
ZERO WIDTH NO-BREAK SPACE)以UTF-16的方式序列化之后,塞到文件的最前边。这样UTF-16解析器读取文件的前两个字节,如果是FE FF
就是大端在前,FF FE
GBK=BDEF
유니코드=U+65A4
GBK=BFBD
유니코드=U +62F7
0x61
CR=0x0D LF=<code>0x0A
(Windows에서 하나의 개행 문자는 CR+LF 두 문자를 차지합니다.)ANSI중국어 간체 시스템에서 ANSI는 중화인민공화국의 국가 표준으로 정의된 GBK 인코딩입니다.
이 파일을 저장하기 위해 ANSI를 사용하는 Windows 메모장 결과는 다음과 같습니다. FF FE 1F 95 A4 65 F7 62 0D 00 0A 00 61 00 0D 00 0A 00
-BOM- ----- ----- ----- ----- ----- ----- ----- -----
U+FEFF 951F 65A4 62F7 000D 000A 0061 000D 000A
간단히 GBK 인코딩을 사용하여 모든 문자를 저장합니다. 가장 높은 비트가 1이 아니고 ASCII와 동등한 단일 바이트, 그렇지 않으면 더블 바이트입니다.
[참고 A]
에서 바이트 순서(Endian) 문제에 주의하세요. 여기서 바이트 순서가 🎜big-endian🎜임을 알 수 있습니다. 🎜🎜하지만 "GBK with big endian first"를 강조할 필요는 없습니다. 왜냐하면 GB2312부터 표준에서는 저장 방법이 big endian 먼저 [Note B]
라고 규정하고 있기 때문입니다. 나중에 GBK는 GB18030-2000과 역호환됩니다. 🎜🎜ANSI의 문제점은 시스템에 따라 다르다는 것입니다. 다른 언어 시스템의 ANSI는 GBK가 아니며 GBK에서 열린 파일은 필연적으로 왜곡됩니다. 그리고 GBK 자체의 문자 집합이 너무 작습니다. 🎜 ("저는 중국어만 사용합니다"라고 말하지 마세요. 유니코드 기호가 없으면 인터넷의 이모티콘을 입력할 수 없습니다.) 🎜🎜유니코드 시리즈🎜🎜Windows 메모장이 "유니코드", "유니코드 빅 엔디안" 및 UTF-8이라고 부르는 것 모두 동일한 유니코드 🎜 인코딩 🎜이지만 🎜바이트 직렬화 🎜 저장 방법이 다릅니다. 🎜🎜UTF-16 및 BOM🎜🎜유니코드는 여기서 UTF-16[Note C]
를 나타냅니다. UTF-16은 매우 간단하고 조잡한 직렬화 방법입니다. 대부분의 유니코드 문자는 U+0000~U+FFFF [참고 D]
범위에 있으며 각 문자는 2바이트를 사용합니다. 디스크에 대한 유니코드 인코딩의 원래 값입니다. 🎜🎜 또한 ASCII 문자는 0x00의 상위 8비트를 저장하기 위해 두 배의 공간을 낭비해야 한다는 점에 유의하세요. 0의 상위 8비트가 생략되면 구문 분석 중에 하이픈을 사용할 수 있는 다른 기반이 없기 때문입니다. 🎜🎜UTF-16의 경우 빅 엔디안과 리틀 엔디안의 문제가 있습니다. UTF-16은 바이트가 빅 엔디안인지 리틀 엔디안인지 먼저 지정하지 않습니다. 그런데 UTF-16에는 바이트 순서를 나타내는 정보가 없기 때문에 어떤 파싱이 왜곡되지 않았는지 수동으로 확인할 수는 없습니다... 유니코드가 제공하는 해결책은 🎜너비 하이픈이 아닌 공백 문자🎜()를 넣는 것입니다. U+FEFF ZERO WIDTH NO-Break Space)는 UTF-16으로 직렬화되어 파일 앞에 채워집니다. 이런 식으로 UTF-16 파서는 파일의 처음 2바이트를 읽습니다. FE FF
이면 큰 끝을 먼저 의미하고, FF FE
는 작은 끝을 의미합니다. 첫 번째. 🎜🎜이 안에 들어있는 것을 BOM(Byte Order Mark)이라고 합니다. 🎜🎜다양한 상황에서 단어 제한을 깨기 위해 🎜너비 하이픈 없는 공백 문자🎜도 유효한 문자로 자주 사용된다는 점은 언급할 가치가 있습니다. SegmentFault의 Q&A 및 의견이 포함되어 있습니다. 🎜🎜메모장의 "유니코드"와 "유니코드 빅엔디안"🎜🎜"유니코드"만 쓴다고 해서 저장 방식이 완전하게 표현된 것은 아닙니다. 여기에는 🎜인코딩🎜만 포함되고 🎜바이트 직렬화🎜는 포함되지 않기 때문입니다. 🎜🎜M$ 이런 오류가 발생하는 것은 전혀 놀랍지 않습니다. 결론만 기억하세요. 🎜Windows 메모장의 "유니코드"는 UTF-16🎜입니다. 🎜Windows Notepad使用“Unicode” = 小端在先的UTF-16,存储这个文件的结果如下:
FF FE 1F 95 A4 65 F7 62 0D 00 0A 00 61 00 0D 00 0A 00 -BOM- ----- ----- ----- ----- ----- ----- ----- ----- U+FEFF 951F 65A4 62F7 000D 000A 0061 000D 000A <p>Windows Notepad使用<strong>“Unicode big endian” = 大端在先的UTF-16</strong>,存储这个文件的结果如下:</p><pre class="brush:php;toolbar:false"> FE FF 95 1F 65 A4 62 F7 00 0D 00 0A 00 61 00 0D 00 0A -BOM- ----- ----- ----- ----- ----- ----- ----- ----- U+FEFF 951F 65A4 62F7 000D 000A 0061 000D 000A <h3>UTF-8</h3><p>UTF-8是一种用1~4个字节表示1个Unicode字符的<strong>变长的</strong>字节序列化方法。具体的实现细节看这篇文章。UTF-8的好处在于:</p><ol class=" list-paddingleft-2"> <li><p>无论是IETF的推荐,还是实际业界的执行,UTF-8都是互联网的标准。</p></li> <li><p>向下兼容,ASCII字符UTF-8序列化后仍是原样,任何ASCII文件也是有效的UTF-8文件。</p></li> <li><p>没有字节序问题。UTF-8的字节序是由RFC3629定死的。</p></li> </ol><p>Windows Notepad使用UTF-8存储这个文件的结果如下:</p><pre class="brush:php;toolbar:false"> EF BB BF E9 94 9F E6 96 A4 E6 8B B7 0D 0A 61 0D 0A --BOM--- -------- -------- -------- -- -- -- -- -- U+ FEFF 951F 65A4 62F7 000D 000A 0061 000D 000A <p>注意UTF-8前边仍然塞进去了<code>U+FEFF</code>按照UTF-8序列化的结果<code>EF BB BF</code>,作为前边提到过的<strong>BOM</strong>字节顺序标记。<strong>Windows Notepad存储的UTF-8,是带有BOM标记的UTF-8</strong>。</p><p>但是如果仅仅对于UTF-8而言,字节序是没有意义的。因为UTF-8的字节序被规范写死,<code>U+FEFF</code>编码后必然得到<code>EF BB FF</code>,得不出其他的。没有二义性,BOM就失去了原本的意义。也许只有区别UTF-8文件和UTF-16文件的用处……</p><p>如何对待UTF-8文件的BOM,RFC3629的第6章有详细的规定,不加详述。</p><p>值得一提的是,BOM我想很多PHP程序员都经历过并且恨之入骨——PHP不认识文件中的BOM头并会将其作为HTTP Response的正文送出。这甚至在无缓冲的情况下,会导致<code>header()</code>等必须在Response开始前执行的函数直接失效。</p><p>所以PHP程序员总是会喜欢<strong>UTF-8 without BOM</strong>的编码方式——这基本也就宣布了Windows下的PHP开发,Windows Notepad完全的淘汰出局,哪怕是任何一星半点代码的临时修改。</p><h2>番外:Notepad++的字符编码测试</h2><p>ANSI没有区别,但Notepad++支持选择多国编码的不同ANSI编码方式(类似浏览器里选编码),可以轻松生成或读取Shift-JIS等其他字符集的文件。适合用于对付日文老游戏的<code>README</code>等文档。</p><p>UCS-2 Big Endian、UCS-2 Little Endian和前边UTF-16的两个例子一致。注意UTF-16的文件不提供“无BOM”的存储方法(提供了就坏了)。</p><p>UTF-8仍然代表“带有BOM标记的UTF-8”。但同时提供PHP程序员最爱的UTF-8 without BOM,就像:</p><pre class="brush:php;toolbar:false"> E9 94 9F E6 96 A4 E6 8B B7 0D 0A 61 0D 0A -------- -------- -------- -- -- -- -- -- U+ 951F 65A4 62F7 000D 000A 0061 000D 000A <p>Simple and clean.</p><p>注解<br>[注A] 对于一个双(多)字节的数,一定会按8位截断为1字节后写盘。那么写盘时先写最低8位还是先写最高8位,就是所谓的“字节序”(Endian)问题。例如,数0x01020304写盘时,是先写最低8位的04 03 02 01,还是先写最高8位的01 02 03 04?<br> 先写低8位的叫做小端在先(little-endian),先写高8位的叫做大端在先(big-endian)。实际采用何种字节序受系统环境、标准规范和软件实际编写的多方面控制,不一概而论。<br>[注B] 字节序如果我没弄错,是GB2312采用的EUC字符编码方法控制的。<br>[注C] 本文并不严格区分UTF-16与UCS-2。<br>[注D] Unicode的最大值实际上达到了U+10FFFF,超出了两个字节能够存储的限度。<br> 但Unicode由于历史原因,留下了U+D800~U+DFFF这一段永久保留不用的空缺区域。<br> 因此对U+10000及以上的字符,UTF-16借助了这部分空缺区域,对这些编码超大的字符打破2字节16位的惯例,特别的用4字节32位去表示之。<br> 这一部分编码值太大的字符,超出了GBK的字符集范围,因此本文将完全忽略。如有机会再进一步测试。</p><p class="comments-box-content"></p>
위 내용은 Windows 메모장의 선택적 문자 인코딩에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!