摘要:為了消除文件格式和字符編碼的疑惑,上網搜索并翻出以前收藏的文章,心結總算了卻。本文即是對文件和字符編碼的個人總結。雙字節多字節字符,編碼方式和字節序,才是困擾程序員的問題。使用兩個字節表示一個字符,平臺默認的字符編碼方案都是。
轉載請注明來源:https://tlanyan.me/ascii-bina...
近期對識別文件格式感到好奇,不幸和字符編碼搞混,不明其中原理導致心煩意亂。為了消除文件格式和字符編碼的疑惑,上網搜索并翻出以前收藏的文章,心結總算了卻。本文即是對文件和字符編碼的個人總結。
文本文件和二進制剛開始的疑惑是:文本文件和二進制文件有什么區別?為什么一個能顯示內容,另一個的內容經常無法(用文本編輯器)正常顯示?
馬里蘭大學的這篇培訓筆記,把兩者的區別講得清楚:文本文件是二進制文件的一種,底層存儲也是0和1;文本文件可讀性和移植性好,但表現字符有限;二進制文件數據存儲緊湊,無字符編碼限制。文本文件基本上只能存放數字、文字、標點等有限字符組成的內容;二進制沒有字符約束,可隨意存儲圖像、音視頻等數據。
用存儲數字的例子可以形象的看出文本文件和二進制文件存儲內容上的差異。例如要存儲數字1234567890,文本文件要存儲0-9這十個數字的ASCII碼,對應的十六進制表示為:31 32 33 34 35 36 37 38 39 30,占用10個字節;1234567890對應的二進制為“?0100 1001 1001 0110 0000 0010 1101 0010?”,占用4個字節(二進制表示32位,一個字節8位),存儲到文件的16進制表示為(大端):49 96 02 D2。
文本文件按字符存放內容,二進制按字節存放,這是兩種文件最本質的區別。根據這個特性,可以推斷出一些常見結論:二進制文件常常比文本文件緊湊,占用空間少;文本文件更友好易用,能用所見即所得的方式編輯;二進制文件常常需要專用程序打開,等等。
回過頭看文本編輯器打開二進制文件常常是亂碼的現象。例如一個二進制文件存放了一個整數1234(四個字節),用16進制表示為:00 00 04 D2。文本編輯器打開后逐個字符解釋,會發現這幾個字節拼不出可顯示的字符,只好亂碼相待。亂碼的原因是文本編輯器不能正確解析字節流,這也是二進制文件需要用專用軟件打開的原因。例如jpg文件要用看圖軟件打開,如果用音樂播放器打開,完蛋!視頻文件要用播放器打開,用壓縮軟件打開,歇菜!
文件格式了解文本文件和二進制文件的區別后,再來看文件格式。我們知道,Windows按文件拓展名識別文件格式,并調用對應的程序打開文件;(類)Unix系統,拓展名可有可無,那么怎么知道這個文件是什么格式呢?
幸好有file命令,這個命令可以告訴我們文件到底是什么格式。文件拓展名不是文件格式的本質區別,內容才是。把a.zip改成a.txt/a.jgp/a.mp3,無論什么文件名,file都讓其原形畢露:Zip archive data, at least v1.0 to extract。
file命令的工作原理可這篇文章。
編碼說完了文件,再來說文件內容中的編碼。常見的127個ASCII字符,沒啥編碼好說的,反正幾乎所有的編碼方式都兼容它。雙字節、多字節字符,編碼方式和字節序,才是困擾程序員的問題。一個漢字,GBK編碼需要兩個字節,還要考慮本機的大小端,才能確定存放的最終形式;網絡通訊時,要轉換成網絡字節序(大端序),接收方才能正常解析。開發人員如果對字符編碼不熟悉,通信時遇到亂碼問題,調試就很困難。
UCS(Universal Multiple Octet Coded Character Set)標準的制定,讓開發人員遠離混亂的多字節字符集。UCS標準里,所有的字符都有唯一的碼點(Code Point),根據碼點就可查到對應字符。UCS用兩個字節表示一個碼點(UCS-4標準是4個字節),對應一個字符。由于使用了兩個字節,可容納2^16-1(6w+)字符,基本上容下各國常用的字符(UCS-4理論上可容納上二十億個字符,目前收納超過16W個字符)。注意UCS只是一個標準,規定了碼點與字符的一一對應關系,但沒有定義如何存儲在計算機中。
規定Unicode字符存儲方式的工作由UTF(Unicode Transformation Format)完成,應用最多的方案是UTF-16和UTF-8。UTF-16使用兩個字節表示一個字符,Windows, MacOS, Java平臺默認的字符編碼方案都是UTF-16。由于有兩個字節,便存在大端和小端兩種方案的區分。只有ASCII字符的文件,使用UTF-16編碼存在空間浪費嚴重的現象(浪費50%的存儲),由Ken Thompson(C語言發明人)和Robe Pike(Go語言發明人)提出的UTF-8編碼方案很快流行起來。UTF-8是單字節流,不存在字節序問題,也不需要BOM。目前UTF-8是web通行標準。
對應關系USC-2的取值范圍是U+0000~U+FFFF,與UTF-8的對應關系如下:
十六進制 | 二進制 |
---|---|
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
從編碼可以看出,與二進制相比,浪費了很多空間。不過這也沒辦法,可顯示的字符更容易閱讀和理解,人類很難抗拒這個誘惑。
UTF-8轉換規則為: 1. 如果某字節第一位是 0 ,那么判定為 ASCII 字節,除了 0 外余下的 7 位是 ASCII 碼,所以 UTF-8 是兼容 ASCII 碼的; 2. 如果第一個字節是 1 ,那么連續的幾個 “1” 代表從這個字符開始,后面連續的幾個字節其實是一個字位,且后面的字節都要以10開頭。
了解如上規則,我們的程序便可輕松的處理UTF-8編碼的字節流。例如要找出“中”的UTF-8編碼,則可以這樣處理(注意文件是UTF-8編碼):
$char = "中"; $length = strlen($char); $bytes = pack("a" . $length, $char); echo "UTF-8:" . bin2hex($bytes) . " "; // 或者 echo "UTF-8:"; for ($index = 0; $index < $length; ++ $index) { echo bin2hex($char{$index}); } echo PHP_EOL;
也可以寫出針對UTF-8編碼的strlen函數:
function myStrlen(string $string) { $slen = strlen($string); $mlen = 0; $maxByteLength = 4; $maxOffset = 7; for ($i = 0; $i < $slen; ++ $i) { $byte = ord($string{$i}); // 從01xxxxxx開始對比,直到11110xxxx 10xxxxxx 10xxxxxx 10xxxxxx。只需要對比第一個字節即可 for ($offset = 0; $offset < $maxByteLength; ++ $offset) { $result = $byte & (1 << ($maxOffset - $offset)); if ($result === 0) { $i += $offset; ++ $mlen; break; } } } return $mlen; } $string = "Coder不是工程師!"; echo "mb_strlen:" . mb_strlen($string) . " "; echo "mStrlen:" . myStrlen($string) . " ";
了解其原理,亂碼不再困惑和迷茫。
參考https://www.cs.umd.edu/class/...
http://www.unicode.org/faq/ut...
https://my.oschina.net/goal/b...
http://mp.weixin.qq.com/s/2H6...
https://www.lifewire.com/file...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28373.html
摘要:下文件名長度限制出現場景在迭代中有一個需求是將文件名修改為所有班級的名稱集合,出現的班級過多導致的文件名過長在下無法創建文件和文件夾的情況解決方式經過查證,中文件名最長為字符,文件路徑最大長度為字符。這是被編碼方案決定的,通過來指定。 Linux下文件名長度限制 出現場景:在迭代中有一個需求是將pdf文件名修改為所有班級的名稱集合,出現的班級過多導致的文件名過長在linux下無法創建文...
摘要:只有徹底理解編碼,遇到編碼問題才知道問題的根源在哪里,并找到對應的解決辦法。花一點時間去徹底消化并理解他,長遠來看,對以后工作效率的提升是非常值得的。比如中國就制定了等編碼規范。 只要涉及編程工作,編碼是永遠繞不開的問題。只有徹底理解編碼,遇到編碼問題才知道問題的根源在哪里,并找到對應的解決辦法。花一點時間去徹底消化并理解他,長遠來看,對以后工作效率的提升是非常值得的。下面是我對編碼的...
摘要:前后端交互過程中涉及的編碼首先,瀏覽器的設置里有設置編碼格式,一般設置為。按照設置的順序檢查檢測文件的編碼。 起因 最近在寫PHP,本身對PHP不太熟練。然后遇到編碼這個問題,困擾了大半天,索性,系統探索解決一番。 前后端交互過程中涉及的編碼 Browser cilent: 首先,瀏覽器的設置里有設置編碼格式,一般設置為UTF-8。 AJAX request: AJAX異步請求的過程...
閱讀 890·2023-04-26 03:03
閱讀 2213·2021-10-12 10:12
閱讀 1206·2021-09-24 09:48
閱讀 1653·2021-09-22 15:25
閱讀 3337·2021-09-22 15:15
閱讀 921·2019-08-29 16:21
閱讀 1072·2019-08-28 18:00
閱讀 3432·2019-08-26 13:44