整理 Windows 的字元編碼與換行符 - Shift_JIS / UTF-8 / UTF-16、亂碼、CRLF / LF,為何混亂
· 小村 豪 · Windows, 字元編碼, 亂碼, 換行符, UTF-8, CP932, PowerShell, Unicode
Windows 的文字周邊的諮詢中相當頻繁以下話題一起混在。
Shift_JIS和UTF-8有什麼不同- 為何發生亂碼
CRLF和LF有什麼不同- 明明已用
UTF-8,為何有讀不了的情況 - 為何同一個 file 在 editor、console、Excel、Git 看起來不同
這個話題不是因日文或中文難而發生。大部分是因 把同一位元組序列以不同前提讀 或 把誤讀的內容原樣儲存。
再加上 Windows 上 Unicode 的世界與 code page 的世界至今共存。在那上面再疊 BOM、換行符、editor 的自動判定、console 的 code page、Git 的換行轉換,話題看起來複雜。
本文以實務向整理 Windows 常混在一起的 Shift_JIS / UTF-8 / UTF-16、為何發生亂碼、CRLF 與 LF 的差異,以及為何這個話題容易混亂。
內容以 2026 年 4 月時點的 Microsoft Learn、PowerShell、Git、W3C / Unicode 系的公開資訊為前提。詳情請參照末尾的參考資料。
1. 先掌握的
先排結論,重要的是以下 7 點。
- text file 不是 文字本身,而是由 bytes + 字元編碼 + 換行符 組成。有時還會加 BOM。
- 亂碼發生在 把同一 bytes 以不同字元編碼 decode 時。
- 換行問題發生在 字元編碼正確但行分隔前提偏離 時。
Unicode和UTF-8不同義。Unicode是字元集合的話,UTF-8或UTF-16是 encoding 的話。- Windows 上被稱為「
Shift_JIS」的,實務上意識為 CP932 / Windows 系的日文 code page 比較不易偏離對話。 - 光說
改成 UTF-8作為規格還不夠。要到決定 BOM 的有無 和 換行符 才變成運營規則。 - 混亂的根源不是 日文本身,而是 不同歷史的多個前提留在同一 Windows 上。
簡言之,處理 Windows 的 text 時,出發點是分開想以下 4 個。
- 該 file 的 bytes 是什麼
- 以哪個 encoding 寫入
- 以哪個 encoding 讀取
- 換行是
CRLF/LF哪個
光分開就會相當不易迷惘。
2. 拆解用語
2.1 Unicode / UTF-8 / UTF-16 / CP932 有什麼不同
先把用語拆解一次比較快。
| 用語 | 指什麼 | 例 | 常見的混淆 |
|---|---|---|---|
| Unicode | 用編號表示字元的框架 | U+3042 (あ) |
以為與 UTF-8 相同 |
| UTF-8 | 把 Unicode 變成 bytes 的 encoding | E3 81 82 |
以為是 Unicode 本身 |
| UTF-16LE | 把 Unicode 變成 bytes 的 encoding | 42 30 |
與 menu 上的 Unicode 混在 |
| CP932 | Windows 日文系的 legacy code page | 82 A0 |
以為與 Shift_JIS 完全相同 |
| CRLF / LF | 行的分隔 bytes | 0D 0A / 0A |
以為是 encoding 的一種 |
| BOM | file 開頭的識別用 bytes | EF BB BF 等 |
以為是 encoding 名本身 |
例如 あ 這 1 個字元,bytes 每個 encoding 也不同。
字元: あ
UTF-8 : E3 81 82
CP932 : 82 A0
UTF-16LE : 42 30
重要的是 字元和 bytes 是不同物。應用程式在畫面上處理看起來是「字元」的東西,但儲存或通訊最終交換的是 bytes。事故通常發生在那個轉換的邊界。
2.2 Shift_JIS 和 CP932 如何思考
現場把日文 Windows 的 text file 統稱為「Shift_JIS」的情況相當多。對話上通,但實務上稍微粗略。
想準確意識 Windows 的日文 legacy text 的話,看作 CP932 或 Windows 系的日文 code page 比較安全。
這裡粗略,以下對話會偏離。
- 說
以 Shift_JIS 儲存,但對方想的是 Windows 側的 CP932 - Linux / macOS 側當
shift_jis處理,但 Windows 源 file 的一部分重現不合 - 聽到
ANSI儲存,但是哪個 code page 依環境而定
所以規格或調查備忘錄盡量如下寫比較安全。
- 不是
Shift_JIS而是CP932 - 不是
ANSI而是ACP (active code page) / 日文環境通常是 CP932 - 不是
text而是像UTF-8 no BOM, LF這樣具體化
2.3 ANSI、Unicode、UTF-8N 用語的陷阱
Windows 周邊中用語標籤也是混亂的原因。
特別多的如以下。
ANSI在 Windows 的 UI 或舊說明中出現,但不是 ASCII。多數情況指 該機器的 active code page。Unicode在部分 editor 或工具中 menu 上的Unicode可能意味 UTF-16LE。聽到以 Unicode 儲存未必是UTF-8。UTF-8N在日文圈的 editor 常見,但這通常是為了區別 UTF-8 no BOM 的 UI 標籤。不是 encoding 的正式名稱。
也就是 同一用語每個工具意義會偏離 就是 Windows。這是最初大的混亂點。
3. 為何發生亂碼
3.1 亂碼的真面目
亂碼的真面目相當單純。
- 把字串以某 encoding 變成 bytes
- 把那個 bytes 以不同 encoding 還原為字串
- 前提不一致就變成不同字串
例如把 あ 以 UTF-8 儲存,bytes 如下。
E3 81 82
以 UTF-8 讀就是 あ,但以 CP932 側的前提讀看起來是 縺� 這類的不同字串。
這時壞的不是「日文」而是 decode 的前提。
一行說亂碼的話如下。
把同一 bytes 當不同 encoding 讀。
3.2 顯示崩壞與資料損壞是不同的
這裡重要的是分開 還能還原的階段 和 已經難以還原的階段。
例如以下流程還有還原的可能。
- 把 UTF-8 的 file 以 CP932 打開
- 畫面上看起來是
縺� - 還沒儲存
這階段原本的 bytes 還是 UTF-8 留著。以正確 encoding 重新開可能還原。
危險的是以下。
- 把 UTF-8 的 file 誤讀為 CP932
- 把看起來壞的內容原樣儲存
- 原本的 UTF-8 bytes 消失
到這裡 不是顯示崩壞而是資料損壞。
實務上不要以「亂碼」這 1 句結束,至少要分開以下。
- bytes 本身還正確嗎
- 誤讀的內容是否已再儲存
3.3 掉到無法表示的字元就回不來
另一個危險是把 Unicode 字串掉到 CP932 這類狹窄 code page 時。
這時若對方不存在的字元包含在內,會發生以下之一。
- 被替換為
? - 放入替換字元
- 變成轉換錯誤
- 被靠到不同的相近字元
例如一部分 emoji 或擴展漢字無法直接掉到 CP932。 這個事故不是以「能否讀」,而是以 來回轉換能否回原 來思考。
一旦丟失的資訊,之後知道正確 encoding 也無法還原。
4. 換行字元的差異是什麼
4.1 CRLF / LF / CR
換行也是 bytes。
CR= carriage return =0DLF= line feed =0A- Windows 的 text file 傳統上是
CRLF(0D 0A) - Linux / Unix 系一般是
LF(0A) CR單獨在舊 Mac 系等舊脈絡中出現
表格如下。
| 換行 | bytes | 主要脈絡 |
|---|---|---|
CRLF |
0D 0A |
Windows 的傳統 text file、legacy 工具 |
LF |
0A |
Linux / macOS / 多數開發工具 |
CR |
0D |
相當舊的 legacy data |
4.2 換行與字元編碼是不同問題
這裡相當重要。
換行符與 encoding 是不同問題。
同一個 UTF-8 的 file 換行也能是 CRLF 或 LF。
例如 A、換行、B 這樣的內容 bytes 會如下變化。
UTF-8 + LF : 41 0A 42
UTF-8 + CRLF : 41 0D 0A 42
也就是,
UTF-8但只換行不同CP932但換行是LFUTF-16LE但換行是CRLF
普通可能。
所以 改成 UTF-8 了但還不一樣 時,實際可能 不是 encoding 而是只換行偏離。
4.3 \n 和 file 上的換行 bytes 未必相同
程式設計師視角中樸素容易混亂的是這裡。
source code 中寫 \n 不代表 file 上一定只出 0A。
在語言或執行環境、I/O API 的 text mode 中,在 Windows 上 \n 可能被轉換為 CRLF。
也就是以下可能偏離。
- 原始碼上的換行表現
- 執行時的字串
- file 上儲存的 bytes
- editor 上可見的換行
因此「我以為寫了 LF,但 file 是 CRLF」的事故會發生。
最近的 editor 一般能處理 LF 單獨,但周邊工具、legacy 應用程式、業務運營還殘留 CRLF 前提。
所以換行問題不是「舊話」,現在也在實務中普通出現。
5. 為何 Windows 特別容易混亂
5.1 Unicode 和 legacy code page 共存
Windows 最麻煩的最大理由是這個。
Windows 上,
- 使用 Unicode 的路
- 以 code page 為基礎處理的路
兩者都留著。
較新的應用程式、web、cross-platform 系資產容易靠向 UTF-8,另一方面舊 CSV、TXT、日誌、Excel 周邊、業務系統連動仍保留 CP932。 此外一部分輸出或 API 周邊也普通地會出 UTF-16LE。
也就是說,1 台 Windows 中多個 text 文化同居。
5.2 標籤未對齊
增加混亂的比起技術本身更是標籤的偏離。
- 說
Shift_JIS,實態是 CP932 - 說
ANSI,實態是 active code page - 說
Unicode,實態是 UTF-16LE - 說
UTF-8,實際上 BOM 有無未確定 - 出現
UTF-8N這類 editor 獨自標籤
這裡曖昧,對話看起來通,但實體未對齊。
5.3 只有 ASCII 問題會隱藏
這也大。
UTF-8 與 ASCII 範圍相容,所以只有英數字和符號的 file 即使前提錯也可能「好像能讀」。 CP932 側 ASCII 相當範圍外觀也不易壞,問題不表面化。
結果如下狀態。
- 只英文的 config 看起來沒問題
- 放入 1 行日文的瞬間壞
- 一直潛藏的問題在運營中才首次發火
所以字元編碼事故容易看起來「昨天還動,今天突然壞」。 實際上 以前就有地雷,只是非 ASCII 字元放入的瞬間看見 的情況多。
5.4 file 內容、file 名、console、source file 是不同層
Windows 中把以下合稱為「字元編碼」會迷。
- file 名 / path
- file 的內容
- console 的顯示
- source code file 本身的 encoding
- 執行時的字串形式
- clipboard 或 GUI 部件上的顯示
例如日文 file 名普通可見,但 file 內容可能以 CP932 儲存。 反之 file 本身是 UTF-8,但 console 的 code page 不合,只有顯示壞。
chcp 65001 這類操作基本上對 console 側的前提有效,不是變既有 file 的 bytes 的話。
此外 source code file 是 UTF-8,執行時寫出的 log file 未必是 UTF-8。 在談哪層的 encoding 每次都需要分開。
另外日文 Windows 中 \ 看起來像日元符號,這也容易與字元編碼的話混在。
但多數情況這是 顯示字型或 glyph 的問題,path 分隔或轉義的意義沒變。
5.5 BOM 和換行符也在不同軸發揮作用
光說 UTF-8 還決定一半。
實務上以下也有效。
- 有無 BOM
- 換行是
CRLF還是LF
例如同樣 UTF-8,
- 有 BOM 能讀的 Windows 工具存在
- 有 BOM 會讓首欄加多餘字元的 Unix 系處理存在
- 只有
LF的 legacy 側難處理的工具存在 CRLF會讓 shell script 或 diff 變粗的情況
這樣 就算 encoding 合了還會出事。
5.6 工具擅自改
更麻煩的是手邊工具隱性改。
- editor auto-detect
- save 時加 / 去 BOM
- Git 轉換
CRLF/LF - shell 或 command 以預設 encoding 儲存
- CSV export 用預料外的 code page
- 不同 version 的 PowerShell 或 tool 預設不同
也就是作業者沒明示,某層擅自加前提 就是 Windows 實務。
這就是「我什麼都沒改卻壞」的真面目。 實際上不是人,是 工具的預設值 改的情況不少。
6. 常見事故模式
把典型事故列表如下。
| 場面 | 實際偏離的 | 典型症狀 |
|---|---|---|
legacy Windows tool 把 UTF-8 no BOM 的設定 file 當作 ANSI / CP932 |
decode 前提 | 只日文亂碼 |
| 把 CP932 的 CSV 傳給以 UTF-8 為前提的處理系 | decode 前提 | �、decode error、意義不明的日文 |
| 把 UTF-16LE 的 log 傳給 Unix 系 text tool | encoding 前提 | 混入 NUL byte,看起來像二進位 |
把 LF 的 source file 在別環境轉換為 CRLF |
換行前提 | 巨大行末差異、script 的缺陷 |
| 把誤讀的內容原樣儲存 | bytes 本身變成別物 | 無法還原的資料損壞 |
規格只寫 輸出 CSV |
interface 未定義 | Excel 能讀但別工具壞 |
只定 統一為 UTF-8 |
BOM / 換行未定義 | 只部分 tool 失敗 |
特別危險的是 看到顯示崩壞後原樣儲存確定事故 的模式。
7. 實務上減少事故的規則
從這裡開始是以運營規則決定什麼能減少事故。
7.1 決定新 text 的基本線
新 file 中先以 UTF-8 為第 1 候補是妥當的。 但光這樣不夠。
至少決定以下比較安全。
UTF-8 with BOM還是UTF-8 no BOM- 換行是
CRLF還是LF - 誰讀的 file
- 是否需要 legacy Windows 工具相容
- Linux / macOS / CI / container 也讀嗎
例如以 cross-platform 為前提的 source code 或 config,UTF-8 no BOM + LF 容易是第 1 候補。 另一方面,需要配合 Windows 的舊工具或既有運營的話,UTF-8 with BOM 或 CP932 + CRLF 還需要的情況也有。
重要的是比起「什麼是對的」的通論,以 和誰交換 來決定。
7.2 既有 legacy file 不要擅自改
既有 file 是 CP932 的話,日常的小修正順便不要 UTF-8 化比較安全。
安全的是以下運營。
- 既有 file 維持原 encoding / BOM / 換行
- encoding 轉換當成別的遷移任務分開
- 確認轉換對象和下游 consumer 後一併轉換
亂碼事故在善意的「順便現代化」下發生相當多。
7.3 把 encoding 和換行當成 interface 的一部分處理
CSV、TXT、log、設定 file、簡易 protocol 不只是內容,text 格式本身就是 interface。
規格至少寫以下比較安全。
- encoding
- BOM 的有無
- newline style
- header 的有無
- quote / delimiter 的規格
- 用哪個工具驗證過
例如光 CSV 這 3 個字不夠。
到寫 UTF-8 with BOM, CRLF, comma delimiter, 有 header 才讓對話不易偏離。
7.4 在讀寫的邊界明示
程式碼側也不要靠隱性預設值比較安全。
- file read / write 時明示 encoding
- process 間的 text 傳遞也意識 encoding
- export / import 處理也把換行作為規格固定
- 不要把 shell 的粗略 redirect 當正式路徑
特別 Windows「能儲存」和「能以正確 bytes 儲存」不同。
7.5 Git 和 editor 的規則也共享
Git 不是自動修正 encoding 的工具。 另一方面 line endings 可能會轉換。
所以以 repository 為單位決定以下比較安全。
- source code 以
LF為基本嗎 - Windows 專用 text 允許
CRLF嗎 .gitattributes如何固定- 如何共享 editor 設定
分開思考 encoding 和 line endings 很重要。 Git 能對齊換行,encoding 事故仍留。
7.6 不要停在「亂碼了」,說清楚哪裡偏離
現場中以下的改說法相當有效。
- 壞說法:
亂碼了 - 好說法:
看起來 UTF-8 no BOM 的 file 被以 CP932 前提打開 - 壞說法:
行末怪怪的 - 好說法:
LF 的 file 被轉為 CRLF 導致差異增加
光能說出哪裡偏離,調查速度就相當不同。
8. 亂碼・行末差異的調查用這 5 問推進
調查迷惘時回到以下 5 問最快。
- 現在這個 file 的 bytes 是什麼
- UTF-8 嗎
- UTF-8 with BOM 嗎
- CP932 嗎
- UTF-16LE 嗎
- 最初誰以哪種前提寫
- editor
- legacy app
- Excel export
- shell / script
- batch / middleware
- 現在誰以哪種前提讀
- editor 的 auto-detect
- console 的 code page
- library 的 default encoding
- import 側的規格
- BOM 和換行是什麼
- 有 / 無 BOM
CRLF/LF
- 誤讀的內容是否已儲存
- 還只是顯示嗎
- 已再儲存 bytes 消失嗎
這 5 問填完,通常原因會浮現。
9. 總結
Windows 的字元編碼與換行符看起來複雜,不是因日文本身難。 是因 bytes、encoding、BOM、newline、tool 的預設值 分別存在,加上 Windows 上新舊 text 文化同居。
特別想記住以下。
- 亂碼是把同一 bytes 當不同 encoding 讀的結果
- 換行問題與 encoding 是不同軸的話
Shift_JIS、CP932、ANSI、Unicode這些詞不要過度相信- 光說
改成 UTF-8不夠,還需 BOM 和換行 - 顯示崩壞與已再儲存的資料損壞分開思考
- 規格不寫
text,要像UTF-8 no BOM, LF這樣寫
簡言之,處理 Windows 的 text 時不是「字串的話」,而是 如何對齊 bytes 的約定 的話比較實務。
10. 相關文章
11. 參考資料
- Microsoft Learn, Code Page Identifiers - Win32 apps https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
- Microsoft Learn, about_Character_Encoding - PowerShell https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_character_encoding?view=powershell-7.6
- Microsoft Learn, Understanding file encoding in VS Code and PowerShell https://learn.microsoft.com/en-us/powershell/scripting/dev-cross-plat/vscode/understanding-file-encoding?view=powershell-7.6
- W3C Internationalization, Character encodings: Essential concepts https://www.w3.org/International/articles/definitions-characters/
- Git documentation, gitattributes https://git-scm.com/docs/gitattributes
- Git documentation, git-config https://git-scm.com/docs/git-config
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
釐清 Windows 的文字編碼 - 亂碼為什麼會發生,尤其是與 Linux 搭配時什麼地方會偏掉
本文從「亂碼為什麼會發生」的角度,重新整理 Windows 在 CP932、UTF-8、UTF-16、BOM、console code page 與 PowerShell 版本之間的編碼前提差異,並聚焦與 Linux 搭配時最常翻車的場景。透過 4 個調查問題與運維 che...
在 Windows 環境下減少 Codex 亂碼事故的最佳實務 - 先把『指示方式』釘住,再談環境整備
本文整理在 Windows 上讓 Codex 安全處理日文檔案的指示原則:讀檔前先確認 encoding 與 BOM,疑似亂碼禁止臆測儲存,既有檔案維持原狀,僅新建採 UTF-8,並在寫入後重新讀取代表性日文行驗證,把規則沉澱進 AGENTS.md 以減少事故。
ClickOnce 是什麼 - 以實務視角整理機制、更新、適合場面・不適合場面
本文以實務視角整理 ClickOnce 是什麼,從 manifest、快取、更新、簽章的構造,到適合公司內部 .NET 桌面業務應用程式的案件與不適合 machine-wide 或 service、driver 等深度 OS 整合的案件,幫助讀者判斷是否採用並掌握 depl...
用 Windows 沙箱加速 Windows 應用程式開發的驗證 - 以實務向整理管理員權限問題、乾淨環境、權限不足・資源不足的重現
整理在 Windows 應用程式開發中如何運用 Windows Sandbox 加速驗證的實務做法。透過按情境分檔的 .wsb、唯讀輸入與專用 Outbox 寫入分離、在容器內另建標準使用者重現權限不足、以及降低記憶體和關閉 vGPU 製造資源不足偏向,把每次的乾淨環境準備...
Windows 的 DLL 名稱解析機制 - 以實務角度整理搜尋順序、Known DLLs、API set、SxS
從實務角度整理 Windows 的 DLL 名稱解析,說明 loader 在掃描檔案系統前會先處理 DLL redirection、API set、SxS、Known DLLs,並用 SetDefaultDllDirectories 與 LoadLibraryEx 旗標縮小...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。