ByteScope

XML 檔案格式

Extensible Markup Language,通用的結構化文字格式。

MIME 類型
application/xml
副檔名
.xml
分類
文字與標記語言

Magic number(檔案簽章)

以 '<?xml' 宣告開頭的純文字。

關於 XML 格式

XML 是一種由「格式良好」規則定義的文字格式,而不是由任何位元組配置定義:一個根元素、每個元素都要關、屬性值要加引號,還有一小組必須逸出的字元。至於某一份 XML 文件的其他一切——元素叫什麼名字、什麼樣的巢狀合法、這些東西是什麼意思——都定義在標準之外,由 schema 或某個剛好拿 XML 當語法的規格決定。這就是為什麼一大堆彼此無關的東西同時都是 XML:Office 文件、EPUB、RSS 與 Atom feed、SVG、Android 版面配置、XMP 中繼資料、sitemap 和 property list 全都是,而它們沒有一個讀得懂另一個。

檔頭結構,一個位元組一個位元組看

位移長度欄位意義
03UTF-8 byte-order markEF BB BF,選用。它是標準唯一允許出現在宣告前面的東西;第一個角括號之前只要有別的位元組,這份文件就不是格式良好,這條規則值得背起來,因為它把一整類 bug 變成兩秒鐘的 hex 檢查。
02UTF-16 byte-order mark大端序是 FE FF,小端序是 FF FE。和 UTF-8 標記不同,這一個不是選用的:標準規定以 UTF-16 編碼的實體必須以標記開頭,而 UTF-8 的實體只是「可以」。UTF-16 的 XML 很罕見,而且會弄壞任何假設「一個字元一個位元組」的工具。
05XML declaration opener3C 3F 78 6D 6C,宣告開頭的五個字元。它位在位移 0,或是在 UTF-8 標記後面的位移 3,除此之外哪裡都不會。對 UTF-8 內容而言它是選用的,這正是為什麼沒有偵測器能硬性要求它,也是為什麼那麼多 XML 只能靠根元素來辨識。
04UTF-16 declaration without a mark大端序是 00 3C 00 3F,小端序是 3C 00 3F 00。標準的自動偵測附錄列出這些樣式,理由很循環、也很值得體會:解析器得先猜對編碼,才讀得到那份「說明編碼是什麼」的宣告。
5variableDeclaration pseudo-attributesversion 排第一而且是必要的,接著是選用的 encoding,再來是選用的 standalone,順序就是這樣、不能換。encoding 的值是一項「關於這些位元組」的宣稱,不是關於它們的事實;相信它的解析器會在第一個和它矛盾的位元組上中止,而那個位置離真正出錯的地方通常很遠。
05DOCTYPE opener3C 21 44 4F 43,而它位在位移 0 的前提是:沒有宣告、沒有標記、開頭也沒有空白。否則它就落在 prolog 剛好結束的地方——這是老實的答案,也說明了為什麼找它該用解析器而不是位元組位移表。

怎麼跟長得像的格式分開

容易搞混的怎麼分
HTML兩者都是角括號,也都沒有 magic number,但瀏覽器採用的嗅探規則把它們分得很乾淨。XML 是在可有可無的空白之後精確比對位元組 3C 3F 78 6D 6C;HTML 則是比對一張標籤名稱樣式表——DOCTYPE HTML、HTML、HEAD、SCRIPT、IFRAME、TITLE、BODY 等十幾種——而且每一個後面都必須接空白或右角括號才算數。更深層的差別在文件出錯的時候:XML 解析器會停下來,HTML 解析器會自己復原。
SVGSVG 就是 XML。位移 0 也好、其他任何地方也好,沒有任何位元組把它們分開;差別只在根元素叫 svg、而且帶著 SVG 命名空間。一個工具說某檔案是 svg、另一個工具說同一個檔案是 xml,兩邊都是對的,而任何非得二選一的偵測器,做的是編輯決定而不是技術判斷。
Android binary XMLAPK 裡的 AndroidManifest.xml 根本不是文字。它是一種以 chunk 組成的二進位編碼,前四個位元組是 03 00 08 00——小端序的 chunk 型別 3 加上標頭大小 8——後面接文件長度。用文字編輯器打開只會看到二進位雜訊,因為文件裡每個字串都被搬到檔案別處的字串池,元素則用索引去指它們。
Word 文件(docx)大家想在 Office 檔案裡找的那些 XML 確實存在,但磁碟上這個檔案開頭是 50 4B 03 04,因為它是 ZIP。.docx、.xlsx、.pptx 前面沒有任何東西是 XML;那些元件是壓縮檔裡的項目,而其中一個叫 [Content_Types].xml 的項目,才是讓這個套件成為 Office 文件、而不是一個剛好裝了標記的普通 ZIP 的關鍵。

常見的壞法

  • prolog 裡混進垃圾。XML 只允許宣告前面有一個位元組順序標記,除此之外什麼都不行,所以一行空白、指令碼印出來的一句警告,或是內容前面的一頁 HTML 錯誤頁,都會產生每個 XML 開發者都見過的那個錯誤:content is not allowed in prolog。它永遠只代表一件事——第一個角括號前面有東西——而 hex 檢視器幾秒鐘就找得出來。
  • 編碼宣告在說謊。宣告寫 UTF-8、位元組其實是 Windows-1252,或者反過來。一切都解析得好好的,直到第一個超過 0x7F 的字元,此時解析器報無效的位元組序列,然後指著一行畫面上看起來再普通不過的文字——因為那個惹禍的字元在寫出它的編輯器裡顯示得好端端的。
  • 被截斷。XML 沒有長度欄位、也沒有尾標,所以文件被切掉這件事,一直要到解析器把輸入讀完、卻還有元素沒關才發現得了。訊息是資料提前結束,而少了多少東西,光靠這個檔案本身完全推不出來。
  • 匯出資料裡混進控制字元。XML 1.0 在 C0 範圍裡只允許 tab、換行與歸位,其餘一律禁止,就算寫成數值字元參照也一樣。所以帶著 0x00 或 0x0B 的資料庫匯出檔,會產生一個看起來格式良好、肉眼檢查也過關、卻沒有任何一個符合標準的解析器願意接受的檔案。
  • 沒有逸出的 & 與角括號。用字串串接而不是序列化器組出來的標記,只要某個值裡出現 & 或 <,當場就垮。解析器會回報「實體參照後面應該要有分號」,位置指在資料裡而不是標記裡,這也是為什麼要修的地方幾乎從來都不在錯誤指的位置。

這個格式現在還在用嗎

XML 丟掉了它在 2005 年佔住的多數地盤,守住了真正重要的那些。網頁 API 與設定檔被 JSON 取代,接著是 YAML 與 TOML,而 SOAP 在還在跑的地方幾乎都是舊系統。它守住的是文件:任何有混合內容、文字與標記交錯而且順序有意義的東西,用 XML 遠比用資料格式合適,這就是為什麼 Office、ODF、EPUB、DocBook 和 TTML 字幕沒有搬家,也不打算搬。它也守住了「驗證才是重點」的場合,因為 XML Schema、XPath 與 XSLT 在 JSON 那一側沒有真正的對應物——所以任何在處理前必須先對照契約檢查過的格式,不論是政府申報、發票、feed 還是 Android 版面配置,通常還是以 XML 送達。而拿它到 hex 編輯器裡看很不尋常的一點是:它完全沒有標頭,檔案的第一個位元組就已經是內容了。

在這個站上打開 XML 檔

這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。

相關格式

容易和 XML 搞混的格式,可以比對它們的開頭位元組。

格式開頭位元組副檔名
HTML無固定簽章.html
SVG無固定簽章.svg
JSON無固定簽章.json

常見問題

檔案的 magic number 是什麼?

檔案開頭的前幾個位元組,不管檔名寫什麼,它標明的是真正的格式。PNG 一定以 89 50 4E 47 開頭,ZIP 則是 50 4B 03 04。作業系統、瀏覽器和 file 指令讀的都是這幾個位元組,而不是相信副檔名 — 這就是為什麼把檔案改名並不會改變它實際上是什麼。

我要怎麼自己檢查 magic number?

在 macOS 或 Linux 上,xxd -l 16 檔名 會印出前 16 個位元組;在 Windows 上,PowerShell 的 Format-Hex -Path 檔名 -Count 16。或者把檔案丟進本站的檔案類型辨識工具,它會在本機讀取位元組並告訴你是什麼格式,不會上傳任何東西。

為什麼兩種格式的 magic number 會一樣?

因為其中一種是建立在另一種之上。docx、xlsx、pptx、apk、jar 與 epub 骨子裡都是 ZIP 壓縮檔,所以全都以 ZIP 的簽章開頭;要分辨它們得看裡面的項目。Java class 檔和 macOS 的通用二進位檔都以 CA FE BA BE 開頭,靠緊接其後的欄位區分。

有完全沒有 magic number 的檔案嗎?

有。XML、HTML、JSON、SVG、CSV 這類以文字為基礎的格式沒有固定的位元組簽章,所以改用結構來辨識:開頭的 <?xml 宣告、<svg 元素,或是 { 後面接著帶引號的鍵。這種偵測是合理推測而非確定,因此本參考頁會把這些格式標示為結構偵測。

magic number 相符就代表檔案安全嗎?

不代表。它告訴你的是格式,不是內容是否無害 — 一個貨真價實的 PDF 或 docx 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

手上有檔案可能是 XML?用檔案類型辨識工具查出來 — 它在本機讀取 magic number,不上傳任何東西。或者瀏覽所有格式。