文章
為什麼 Excel 會搞壞你的 CSV 檔案
一個以三個零開頭的病歷編號。一個叫做 SEPT2 的基因。一組十六位數的帳號。把這些內容放進純文字的 CSV 檔案,用試算表打開,Excel 的匯入功能不只是顯示它們,還會改寫它們:零不見了,基因名稱變成了日期,帳號少掉了最後幾位數。這些嚴格來說都不算錯誤,而是當一套通用的數字與日期猜測邏輯,遇上只是「長得像」數字或日期的文字時,會發生的事。以下說明 Excel 打開 CSV 檔案的那一刻,實際發生了什麼,以及怎麼在事情發生之前就阻止它。
開頭的零會消失
在儲存格裡輸入 02134,Excel 預設的欄位型別「一般」會把它讀成數字,並去掉開頭的零:2134。任何用數字字元儲存、但本質上不是數量的代碼,都會遇到這個問題:美國郵遞區號、法國郵遞區號、員工編號或發票編號、電話號碼。從技術上說,底層 CSV 檔案並沒有真的丟失什麼,儲存格旁邊的文字維持不變,直到你儲存活頁簿的那一刻,這時 Excel 會把它顯示的內容寫回去,零就永久消失了。解法是在 Excel 有機會猜測之前,先告訴它這一欄是文字:透過「資料」>「從文字/CSV」(或「取得資料」>「從檔案」),可以明確設定每一欄的資料型別,或者在單一儲存格的值前面加上一個單引號,強制以文字處理。

文字會在你不知情的情況下變成日期
同一套猜測邏輯,也會尋找長得像日期的文字。一個內容是 MAR1 或 SEPT2 的儲存格,會被讀成 3 月 1 日或 9 月 2 日,悄悄轉換成日期值並重新格式化,完全不會顯示任何警告。這不是假設情境:2020 年,人類基因命名委員會(HGNC)重新命名了約 27 個人類基因符號,包括 SEPT2 和 MARCH1,原因就是試算表軟體每次打開資料集時,都會把它們變成日期,這個問題嚴重到 2016 年一份針對基因體學論文的調查發現,在附上補充 Excel 檔案的論文中,大約每五篇就有一篇出現這類自動校正錯誤。這個教訓不只適用於基因名稱:任何看起來像「日加月」的簡短英數代碼,只要有人在試算表裡打開你的 CSV,就有風險,不只是你自己打開時。
很長的數字會遺失最後幾位數
Excel 把每一個數字都儲存成精確度為 15 位有效數字的浮點數值,這個限制是微軟自己文件記載的。像 4111111111111111 這樣的十六位數卡號或帳號,會被四捨五入以符合這個限制,所以最後一兩位數會悄悄變成零,數值不再是你當初輸入的那個。事後再重新格式化儲存格,並不能把遺失的數字找回來,因為底層儲存的數字早就已經被捨入;唯一的解法,是在 Excel 把它當成數字處理之前,先把該欄匯入為文字,或在數值前加上單引號。

帶重音的文字會變成亂碼
編碼問題是和自動校正完全不同的另一種故障模式。一個沒有位元組順序記號(BOM)的 CSV 檔案,本質上就是一串位元組;如果它是以 UTF-8 儲存的,Windows 上的 Excel 通常會改用系統自己的字碼頁來假設它,於是帶重音的字母、貨幣符號和表情符號,都會變成亂碼,例如 café 這樣的字串會顯示成類似 café 的東西。為 Excel 產生 CSV 檔案時,安全的做法是存成「CSV UTF-8」,或加上 UTF-8 位元組順序記號;讀取 CSV 檔案時,安全的做法是透過「資料」>「從文字/CSV」匯入,這樣可以明確選擇來源編碼,而不是靠雙擊打開去賭它猜對。
在不用 Excel 打開的情況下搬移資料
嚴格來說,這些都不算是 Excel 的錯,而是試算表的數字與日期猜測邏輯本來就是這樣設計的,而且不管用什麼軟體打開,都會發生,不只是 Excel。要重新整理一份 CSV 檔案,或在它進到任何試算表之前,先確認某一欄真的維持是文字,最安全的做法是直接處理它:我們的資料轉換工具用標準、能處理引號的解析器讀取 CSV,絕不會重新解讀某個值的型別,所以郵遞區號或基因名稱會維持你給它的原始文字。它完全在你的瀏覽器中執行,你貼上的任何內容都不會被上傳到任何地方。不過這只保護了轉換這一步:如果輸出結果之後還是會被試算表打開,那麼你也需要在那裡把敏感欄位標記為文字。
本文涉及的工具
常見問題
如何阻止 Excel 移除 CSV 裡開頭的零?
不要用雙擊直接打開檔案。改用「資料」>「從文字/CSV」(或「取得資料」)匯入,它會顯示逐欄的資料型別選擇器;在完成匯入前,把郵遞區號、編號或電話號碼那一欄設為「文字」。快速的單一儲存格修法,是在值前面輸入單引號,例如 '02134,這會強制以文字處理,不會改變顯示內容。
為什麼 Excel 把我的編號或代碼變成了日期?
Excel 的「一般」欄位型別會主動尋找長得像日期的文字(例如一個短單字加數字,或用連字號、斜線分隔的兩個數字),並轉換任何符合的內容,完全不會事先詢問。2020 年強迫重新命名 SEPT2、MARCH1 等基因符號的,正是這個機制。和開頭零的處理方式一樣,把該欄匯入為「文字」,就能在猜測發生之前阻止它。
換一種方式轉換我的 CSV,能完全避開這些問題嗎?
這只能避開那一次轉換步驟的問題,不是永久解決。像我們的資料轉換工具這種,把每個欄位都當成純文字處理的解析器,在把 CSV 轉成 JSON 時,不會改寫郵遞區號或基因名稱。但如果輸出結果之後還是會被打開成試算表(不管是你自己,還是收到檔案的人),那個程式自己的匯入功能,又會有機會重新解讀這些值,所以敏感欄位在那裡也需要再標記一次文字。