Skip to content

MS_Encoding

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

エンコーディング

概要

  • エンコーディングとは【encoding】(エンコード) - 意味-解説-説明-定義 : IT用語辞典
    https://e-words.jp/w/E382A8E383B3E382B3E383BCE38387E382A3E383B3E382B0.html
    • 情報を一定の規則に従ってデータに置き換えて記録すること。
    • ある形式のデータを一定の規則に基づいて別の形式のデータに変換すること。
    • (文字エンコーディングだけでなく、)データ圧縮や暗号化などもこれに含まれる。

余談:・・・となると、

Stream に Decorate パターンを適用する
殆どの処理が=エンコーディングと言う事になる。

補足(原文の「余談」は的を射ている): .NET の Stream
Decorator パターンで設計されており、
「変換」を層として重ねる構造になっている。

// 圧縮 → 暗号化 → 文字エンコーディング を重ねる
using var file   = File.Create("data.bin");
using var crypto = new CryptoStream(file, encryptor, CryptoStreamMode.Write);
using var gzip   = new GZipStream(crypto, CompressionMode.Compress);
using var writer = new StreamWriter(gzip, Encoding.UTF8);
writer.Write("本文");
【層の重なり】
   string(文字)
     ↓ StreamWriter(文字エンコーディング)
   byte[]
     ↓ GZipStream(データ圧縮)
   byte[]
     ↓ CryptoStream(暗号化)
   byte[]
     ↓ FileStream(永続化)

どの層も「バイト列 → バイト列」の変換であり、
文字エンコーディングだけが特別なわけではない、
という原文の指摘はそのまま正しい。
文字エンコーディングは「最上層=文字とバイトの境界」に位置する点だけが
他と異なる(ここだけ string が絡む)。

文字エンコーディング

ココでは各種、文字エンコーディングの情報を纏めています。
「外字」については、「Windowsの外字」を参照下さい。

文字エンコーディングとは、文字列データを、
異なる文字コード(コードページ)を使用したバイト表現に変換することである。

補足(用語の整理): 「文字コード」という語は複数の層を混ぜて
使われるため、混乱の元になる。厳密には 3 層に分かれる。

意味
符号化文字集合(Coded Character Set) 「どの文字に、どの番号を振るか」 JIS X 0208、Unicode、ASCII
文字符号化方式(Character Encoding Scheme) 「その番号を、どうバイト列にするか」 Shift_JIS、EUC-JP、UTF-8、UTF-16
コードページ Microsoft / IBM が符号化に振った番号 932(CP932)、65001(UTF-8)
【Unicode の場合】
   文字「あ」
     ↓ 符号化文字集合(Unicode)が番号を決める
   U+3042(コードポイント)
     ↓ 文字符号化方式が バイト列にする
   UTF-8   → E3 81 82(3 バイト)
   UTF-16  → 42 30(リトルエンディアン、2 バイト)
   UTF-32  → 42 30 00 00(4 バイト)

「Unicode」と「UTF-8」は同じ層のものではない
Unicode は文字集合、UTF-8 はその符号化方式である。
Windows が言う「Unicode」は、多くの場合 UTF-16 LE を指す
Encoding.Unicode が UTF-16 LE であるのはこのため)。

文字化け

文字化けは、一般的に、

  • エンコーダに適切な文字コード(コードページ)を指定していない
  • プラットフォームやユーザ間で使用している外字が異なる
  • コードページによっては、変換が不可逆なケースがある

際に発生します。

文字コード(コードページ)指定を大きく誤っている場合は、すぐ気が付くのですが、

  • UTF-16(Unicode)
  • UTF-8
  • Shift JIS
  • EUC-JP

一部だけ文字化けする場合のトラブルシュートは苦戦することが多いです。
一部だけ文字化けするパターンは以下の様に分類できると考えます。

  • 文字コード(コードページ)指定を大きく間違えていない(微妙に間違っている)

    • 例えば、Shift_JIS の亜種のコードページ指定を誤っている。
      • ISO-2022-JP
      • Microsoft コードページ932(CP932)
      • MacJapanese
  • 外字領域の定義(見た目)が異なっている。

    • この場合、データのエンコード自体は正しく行われている。
    • 人間にとっての見た目が、異なっているため文字化けと感じられる。
    • 外字についてはこちらを参照:「Windowsの外字

補足(この分類は今も有効): 「大きく間違えた文字化け」と
一部だけの文字化け」を分けるという原文の視点は、
障害切り分けの手順としてそのまま使える。

症状 原因の見当
全部が化ける(「譁�ュ怜喧縺�」等) エンコーディングの指定違い。まず疑う
全部が ? になる 変換先に存在しない文字(例:UTF-8 → ASCII)
一部の記号だけ化ける(〜、−、①、㈱) CP932 の亜種違い、または波ダッシュ問題(事例4)
一部の漢字だけ化ける(髙、﨑) 外字・機種依存文字、または JIS2004
で表示される フォントにその字がない(データは正しい)
1 文字が 2 文字に見える サロゲート ペアの扱い(文字のチェック方式

最後の 2 つは「文字化け」ではない点が重要である。
データは正しく、表示側の問題なので、
データを直そうとすると事態が悪化する。

ポイント

従って、以下に注意を払う必要があります。

  • プラットフォーム

    • 実装されている文字コード(コードページやフォント)
    • 適用される標準のエンコーディングが何か?
    • フォントや外字がインストールされているか?
  • エンコーディングが動作する箇所

    • ファイル I/O

    • SGML 文書の I/O

      • HTML 出力
      • XML 入出力
    • ファイル転送(プロトコルに実装されているケース)

      • SFU(Microsoft Windows Services for UNIX)
      • FTP(ASCII モード)
    • データベースの I/O
      Unicode 文字列以外のフィールドから入出力する場合、
      通常、照合順序で指定したに対応した文字コード(コードページ)
      を使用して、エンコード・デコードされる。

補足(「エンコーディングが動作する箇所」の現在版): 原文の
**「境界を数え上げる」**という発想が本質で、
現在のシステムでも同じ手順で洗い出せる。

【文字とバイトの境界=エンコーディングが働く場所】

 ① ファイル I/O          … StreamReader/Writer の encoding 引数
 ② HTTP                  … Content-Type の charset、URL エンコード
 ③ HTML / XML / JSON     … 宣言と実体の不一致
 ④ データベース          … 列の型(varchar / nvarchar)と照合順序
 ⑤ コンソール            … Console.OutputEncoding、端末のコードページ
 ⑥ プロセス間            … 標準入出力のリダイレクト、引数
 ⑦ 圧縮ファイル          … ZIP のファイル名のエンコーディング(!)
 ⑧ メール                … MIME のヘッダとボディ
 ⑨ CSV / 固定長ファイル  … BOM の有無、改行コード

**現在の指針は「境界を減らす」**である。

・内部は常に UTF-16(.NET の string)で扱う
・外部との入出力は【原則 UTF-8】に統一する
・変換が必要な箇所を【システムの端に集約する】
   → 中間層で変換すると、どこで壊れたか分からなくなる

⑦ ZIP のファイル名は現在も事故が多い。
古い ZIP はファイル名を CP932 で持つため、
UTF-8 前提のツールで開くと化ける(逆も同じ)。

// .NET で CP932 のファイル名を持つ ZIP を読む
ZipFile.ExtractToDirectory("a.zip", "out",
    Encoding.GetEncoding(932));

事例1

プラットフォームに実装されている文字コード(コードページやフォント)に起因する文字化け。

  • Microsoft コードページ932 - Wikipedia
    https://ja.wikipedia.org/wiki/Microsoft%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9A%E3%83%BC%E3%82%B8932

    • インターネット上での Windows-31J の利用について ~ NEC 特殊文字・IBM 拡張文字

      • IBM 拡張文字等の Windows-31J 独自追加の文字は、
        他の JIS X 0208 非登録の CJK 統合漢字に比べて、
        異機種(OS / アプリケーション)間でのデータ交換を、
        文字化けを起こしたりせずにデータのやり取りが正常に行える確率が高い。
      • しかし、JIS X 0208 非登録の文字をサポートしない環境があることを考えると、
        JIS X 0208 登録文字だけを用いてデータ交換を行った方が、問題が起こりにくい。
    • JIS X 0208 - Wikipedia
      https://ja.wikipedia.org/wiki/JIS_X_0208

  • Windowsの機種依存文字 - CyberLibrarian
    http://www.asahi-net.or.jp/~ax2s-kmtn/ref/mdc.html

    • NEC 機種依存文字
    • NEC の IBM 拡張文字
    • IBM 拡張文字

補足(機種依存文字の実際): CP932 が JIS X 0208 に追加した領域
事故の温床である。

領域 内容
NEC 特殊文字(13 区) 丸数字、ローマ数字、単位記号 ① ② Ⅰ Ⅱ ㈱ ℡ №
NEC 選定 IBM 拡張文字(89~92 区) IBM 拡張文字の重複定義 兊 兤 冝 …
IBM 拡張文字(115~119 区) 人名・地名の漢字、記号 髙 﨑 彅 纊

NEC 選定 IBM 拡張文字と IBM 拡張文字は、
同じ文字に 2 つのコードが割り当てられている
(重複符号化)。
これが往復変換で値が変わる原因になる。

「兊」を Unicode → CP932 に変換すると 0xFA5C(IBM 拡張)になるが、
 元データが 0xED40(NEC 選定)だった場合、値が変わって戻る
   → バイト列で比較する処理(チェックサム、重複判定)が壊れる

原文の結論——「JIS X 0208 登録文字だけを用いる」——は
データ交換の指針としては今も正しい

ただし現実には、氏名に「髙」「﨑」が入るため避けられない。
現在の解は、システム全体を UTF-8 / Unicode で通すことである
アプリケーションのUnicode化)。

事例2

プラットフォームで適用される標準のエンコーディングに起因する文字化け。

補足(この差は今も残っている): 原文の指摘は現在も有効で、
.NET と Java の異種混在システムで実際に起こる

指定 .NET Java
"Shift_JIS" CP932 と同じ(= MS932) JIS X 0208 準拠機種依存文字が化ける
"MS932" (この名前は使わない) CP932 相当
"Windows-31J" CP932 CP932 相当
【事故のパターン】
   .NET 側: Encoding.GetEncoding("Shift_JIS") で「①」を書き出す
             → 0x8740(CP932 として正しい)
   Java 側: new String(bytes, "Shift_JIS") で読む
             → 「?」になる(JIS X 0208 に ① がないため)

【対策】 Java 側で "Windows-31J" を明示する

エンコーディング名は「別名」が多く、実体が処理系で異なる——
これが教訓である。
曖昧さのない名前Windows-31JUTF-8)を使い、
両システムの実装を確認する

事例3

フォントや外字のインストール状況に起因する文字化け。

事例4

コードページによっては、変換が不可逆なケースがある。

  • JA16SJIS → Unicode 方向への変換において、
    Unicode の文字データに U+FF5E が渡されると 0x8160 に変換される。

  • 逆向きの JA16SJIS → Unicode 方向への変換において、
    JA16SJIS の入力データに 0x8160 が渡されると、U+FF5E ではなく U+301C に変換される。

この変換はあくまでも Unicode から JA16SJIS 方向への片方向の変換においてのみ適用される。

移行メモ(原文の方向表記が入れ替わっている): 原文は
**1 番目も 2 番目も「JA16SJIS → Unicode 方向」と書いているが、
内容から見て
1 番目は「Unicode → JA16SJIS 方向」**が正しい
(「Unicode の文字データに U+FF5E が渡されると」とあるため)。
最終行の「Unicode から JA16SJIS 方向への片方向の変換」という記述とも整合する。

この現象は「波ダッシュ問題」として知られる、
日本語システムで最も有名な文字化け
である。

【問題の構造】
   Shift_JIS の 0x8160(1 バイト目 0x81、2 バイト目 0x60)
     ↓ この 1 文字に、Unicode の候補が 2 つある

   U+301C  〜  WAVE DASH(波ダッシュ)      ← JIS の規格上はこちら
   U+FF5E  ~  FULLWIDTH TILDE(全角チルダ) ← Microsoft はこちらに対応付けた
対応付け 採用している処理系
0x8160 ↔ U+FF5E Windows / .NET(CP932)、多くの Windows アプリ
0x8160 ↔ U+301C Java の Shift_JIS、Unix 系、Oracle の JA16SJIS、iOS/macOS

同じ「〜」に見えるのに、バイト値が違うため、

・文字列比較が一致しない(見た目は同じなのに)
・DB の検索でヒットしない
・往復変換で別の文字になる
・Windows で入力したデータを Oracle に入れると化ける

同種の問題を起こす文字(Unicode 対応が 2 通りある):

文字 JIS 系 Microsoft 系
〜(波ダッシュ) U+301C U+FF5E
‖(双柱) U+2016 U+2225
−(負符号) U+2212 U+FF0D
¢ £ ¬ U+00A2 U+00A3 U+00AC U+FFE0 U+FFE1 U+FFE2

現在の対策:

① 【システム全体を UTF-8 に統一する】
      → 変換自体をなくす。最も確実

② 変換が避けられないなら、【正規化テーブルを持つ】
      → 入力時に U+FF5E → U+301C 等へ寄せる(どちらかに統一)

③ 【Unicode 正規化(NFKC)を掛ける】
      → ただし NFKC は全角英数を半角にする等、影響が広い。要注意
// ② の例:入力時に波ダッシュ系を統一する
static string NormalizeWaveDash(string s) => s
    .Replace('~', '〜')   // ~ → 〜
    .Replace('-', '−')   // -  → −
    .Replace('∥', '‖');  // ∥  → ‖

コードページ

  • サポートするコード ページ
    https://learn.microsoft.com/ja-jp/windows/win32/intl/code-page-identifiers

  • Microsoft コードページ932 - Wikipedia
    https://ja.wikipedia.org/wiki/Microsoft%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9A%E3%83%BC%E3%82%B8932

    • CP932 の呼称(別名)の整理

      • Windows-31J
        Windows 3.1 (J) のリリースに合わせて、マイクロソフトが
        IBM と日本電気 (NEC) のコードを統合して作った符号化文字集合。
        1993 年以降、マイクロソフトが自社のドキュメント等で「CP932」という用語を使って
        表している対象は、常にこの「Windows-31J」である。この名前は IANA に登録されている。

      • MS932
        Java で、「IBM のコードページ 932」と「Windows-31J」を区別するための用語。

      • CP932
        MS-DOS と Windows における日本語コードページを表す用語。
        「Windows-31J」が制定されるまでは、OEM ベンダによって文字集合が違う。

      • MS 漢字コード
        「CP932」とほぼ同じ意味の用語である。
        マイクロソフトが(Shift_JIS という符号化方式を)策定したという点や、
        マイクロソフトが(JIS X 0208 という文字集合に対して)文字を独自に追加した点を
        強調したい場合に用いられる。また、単に「シフト JIS」のことを指している場合もある。

      • OEM コードページ 932
        Windows 3.1 日本語版の発売以前における、OEM ベンダ各自の拡張を許した仕様の文字セット。
        以下は、マイクロソフトから離れ、現在では公的機関からも認められた文字符号化方式を指す用語。

      • シフト JIS
        JIS X 0208 符号化文字集合を一定の規則に従ってシフトした文字符号化方式。
        具体的な内容は JIS X 0208:1997 に「シフト符号化表現」として記載がある。
        しかし、文脈によってはベンダ拡張されたコードセットを指している場合もある。

      • Shift_JIS
        「シフト JIS」の IANA 登録名。

      • SJIS
        Shift_JIS の短縮形。Java では Shift_JIS と同義語。

移行メモ(SJIS は Java では Shift_JIS と同義ではない): 原文の
最終行は正確でない。Java の SJISShift_JIS の別名として
扱われるが、事例2で述べた通り、
Shift_JIS 自体が CP932 とは異なるため、
「同義語」という表現は誤解を招く。
Java で CP932 相当を使いたければ Windows-31J または MS932 を指定する。

補足(.NET でコードページを使う際の必須手順): .NET Core 以降、
Encoding.GetEncoding(932) は既定では失敗する

// ✗ .NET Core / .NET 5+ では ArgumentException
var enc = Encoding.GetEncoding(932);

// ○ プロバイダーを登録してから使う
// NuGet: System.Text.Encoding.CodePages
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var enc = Encoding.GetEncoding(932);

.NET Framework には全コードページが組み込まれていたが、
.NET Core 以降は UTF 系と ASCII / Latin-1 のみが標準で、
それ以外は上記のパッケージが要る(クロス プラットフォーム化のため)。

主なコードページ:

番号 名前 内容
932 Shift_JIS / Windows-31J 日本語(CP932)
20932 EUC-JP 日本語(Unix 系)
50220/50222 ISO-2022-JP JIS コード(メールで使われた)
1252 Windows-1252 西欧
65001 UTF-8 既定にすべきもの
1200 UTF-16 LE Windows の内部表現(Encoding.Unicode

Encoding.Default の意味も変わった点に注意する。

.NET Framework .NET Core 以降
Encoding.Default OS の ANSI コードページ(日本語なら CP932) 常に UTF-8

旧コードから移植する際、Encoding.Default
暗黙に CP932 を意味していた箇所は全て壊れる

移植時は明示的に Encoding.GetEncoding(932) へ置き換える

その他のエンコーディング

SGML文書

HTMLエンコーディング

文字参照(数値文字参照・文字実体参照)のエンコーディング(エスケープ)を行う。

  • 文字参照 - Wikipedia
    https://ja.wikipedia.org/wiki/%E6%96%87%E5%AD%97%E5%8F%82%E7%85%A7\ 直接記述できない文字や記号を HTML・XML 上に表記、参照するため用いられる方法。
    • マークアップで使われる、半角の不等号「<」や「>」

    • 指定の文字コードで表現できない文字を表記、参照する。

    • 表記方法により「数値文字参照」と「文字実体参照」の二種が存在する。

      • 数値文字参照
        10 or 16 進数で該当文字を ISO 10646 の文字番号で指定する方法。
        指定の文字コードで表現できない文字を表記、参照する。

      • 文字実体参照
        特定のキーワード文字列で該当文字(主にマークアップで使われる文字)を指定する方法。
        XSS(クロスサイト・スクリプティング)対策としても使用される。

補足(エスケープの正しい考え方): 原文は
「XSS 対策としても使用される」と書いているが、
ここは現在の理解に沿って整理しておく必要がある

**XSS 対策の本質は「出力先の文脈に応じたエスケープ」**であり、
HTML エスケープさえすれば安全、ではない

出力先(文脈) 必要なエスケープ
HTML の本文 < > & " ' → 文字参照
HTML の属性値 上記+必ず引用符で囲う
<script> の中 JavaScript のエスケープ(HTML エスケープでは不十分)
URL の一部 URL エンコード(後述)
CSS の中 CSS のエスケープ
<a href> の値 スキームを検証javascript: を弾く)
<!-- HTML エスケープしても防げない例 -->
<a href="&#106;avascript:alert(1)">クリック</a>

.NET での実装:

// 文脈ごとに使い分ける
HttpUtility.HtmlEncode(s);        // HTML 本文・属性
HttpUtility.JavaScriptStringEncode(s);  // <script> 内
HttpUtility.UrlEncode(s);         // URL

ASP.NET Core の Razor では @値 が自動的に HTML エスケープされる
ため、手でエスケープしないのが原則である
(二重エスケープになる)。
危険なのは @Html.Raw() を使った箇所だけ、という状態に保つ。

HTMLの文字エンコーディング

HTTP ヘッダか、meta タグにエンコーディングを指定することで
WWW ブラウザがデコードする際に指定する文字コード(コードページ)を指定する。

  • HTTP ヘッダ

    Content-Type: text/html; charset=Shift_JIS
    
  • meta タグ

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  • @IT:Javaの文字化け対策FAQ(1)
    http://www.atmarkit.co.jp/fjava/rensai3/mojibake01/mojibake01.html

    • 質問1:Web ブラウザが文字コードを判定する基準は何ですか?
      HTTP ヘッダか、meta タグにエンコーディングを指定
    • 質問2:page ディレクティブで文字コードを正しく指定する方法は?
      CGI 毎、HTML を出力する際に指定する方法が存在する。

補足(現在の書き方と優先順位): HTML5 では 短い形式が使える。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">  <!-- ← HTML5 の書き方。先頭 1024 バイト以内に置く -->

判定の優先順位(強い順):

 ① HTTP ヘッダの Content-Type: charset=…   ← 最も強い
 ② BOM(バイト オーダー マーク)
 ③ <meta charset="…">
 ④ ブラウザの推測 / 既定値

① が ③ を上書きするため、
HTML に UTF-8 と書いても、サーバが別の charset を返せば化ける
ここが実務でよく詰まる点である。

現在の指針:

・【UTF-8 以外を使わない】
    HTML Standard は UTF-8 を要求している
・HTTP ヘッダと meta の両方を UTF-8 に揃える
・meta charset は【head の先頭付近】に置く
    (1024 バイトを過ぎるとブラウザが再解析する)
・BOM は付けない(UTF-8 では不要。付けても害は少ないが不要)

ASP.NET Core では既定で UTF-8 が使われるため、
明示的に変更していなければ問題は起きない

XMLの文字エンコーディング

ヘッダ部分の記述を DOM が理解して自動的にエンコード・デコードします。

<?xml version="1.0" encoding="Shift_JIS"?>

移行メモ(原文の XML 宣言に閉じ記号の誤りがある): 原文は
<?xml version="1.0" encoding="Shift_JIS"> と書いているが、
XML 宣言は ?> で閉じる必要がある
(上のコードでは修正済み)。

補足: XML では宣言と実体が食い違うと、パーサがエラーにする
(HTML のように黙って推測しない)。これは良い性質である。

・encoding を省略した場合、【UTF-8 と見なされる】
・BOM があれば、BOM が優先される
・宣言と実体が違えば、パース エラー

.NET で XML を書き出す際、
XmlWriterSettings.Encoding と実際の書き込み先を揃える

var settings = new XmlWriterSettings { Encoding = new UTF8Encoding(false) };
using var writer = XmlWriter.Create("out.xml", settings);

new UTF8Encoding(false)falseBOM を付けない指定である。
Encoding.UTF8BOM 付きなので、
意図せず BOM が入って相手システムが読めなくなる事故が起きる。

URLエンコーディング

補足(.NET のエンコード API の使い分け): URL エンコードには
微妙に仕様の違う API が複数あるため、混同すると事故になる。

API 空白の扱い 用途
Uri.EscapeDataString %20 RFC 3986 準拠。これを使う
Uri.EscapeUriString %20 廃止済み(Obsolete)。使わない
HttpUtility.UrlEncode + application/x-www-form-urlencoded(フォーム)
WebUtility.UrlEncode + 同上(System.Net 側)
Uri.EscapeDataString("あ い");        // "%E3%81%82%20%E3%81%84"
HttpUtility.UrlEncode("あ い");       // "%e3%81%82+%e3%81%84"

+ を空白と解釈するのはフォーム送信の規則であり、
**URL のパス部分では + は「プラス記号そのもの」**である。
パスに HttpUtility.UrlEncode を使うと、
空白が + になって相手に別の文字列として届く

【使い分け】
   URL のパス・クエリの【値】   → Uri.EscapeDataString
   フォームの POST ボディ       → HttpUtility.UrlEncode

文字コードにも注意する
URL エンコードはバイト列を %XX にするだけなので、
元のバイト列が UTF-8 か CP932 かで結果が変わる
現在は UTF-8 が既定(RFC 3986 が推奨)だが、
古いシステムは CP932 で送ってくることがある。

Base64エンコーディング

  • 概要

    • Byte データと文字列の相互変換を行う。
  • 利用シーン

    • Byte データを Text ベースのプロトコル(SMTP, HTTP 等)に乗せる場合に使用する。
  • 参考

補足(Base64 の実際): Base64 は暗号化ではない——
誤解が多いので明記しておく。誰でも復元できる

string s = Convert.ToBase64String(Encoding.UTF8.GetBytes("秘密"));
// "56eY5a+G"  ← 単に読みにくいだけで、秘匿性はない

サイズが約 4/3 に増える点も重要である
(3 バイト → 4 文字)。大きなデータには向かない。

URL で使う場合は「Base64 URL セーフ」が要る

【標準の Base64】       + / =  ← URL で意味を持つ文字が入る
【Base64 URL セーフ】   - _    ← + → -、/ → _、= は除去

JWT やトークン系はすべて URL セーフである。

// .NET 9 以降は標準 API がある
var s = System.Buffers.Text.Base64Url.EncodeToString(bytes);

// それ以前は自前で置換する
var s = Convert.ToBase64String(bytes)
    .Replace('+', '-').Replace('/', '_').TrimEnd('=');

性能が要る場面では Convert.TryToBase64Chars を使うと
中間の文字列割り当てを避けられる。

各種フォーマット

Excelが開くことが可能なCSVファイル

CSV ファイルを扱い際は、以下のことに気を付ける。

Excel にて文字化けやエラーが発生せず読み込める CSV ファイルは、

以下の 2 種類のエンコードのファイルである。

  • BOM 付き UTF-8
  • 各言語(国)の ANSI コード

各言語(国)の ANSI コード(一例)

# 言語 文字セット名 codepage
1 日本語 Shift-JIS 932
2 英語(米国) Windows-1252 1252
3 英語(英国) Windows-1252 1252
4 フランス語(フランス) Windows-1252 1252
5 スペイン語(スペイン) Windows-1252 1252
6 繁体字中国語 big5 950
7 簡体字中国語 gb2312 936
8 韓国語 ks_c_5601-1987 949

Excel 上で CSV 形式で保存すると、ANSI コードで保存される。

補足(CSV と Excel の現況): 原文の「BOM 付き UTF-8」という指摘が
最も重要な実務知識で、現在も変わらない。

・BOM なし UTF-8 の CSV を Excel でダブルクリックすると
   → 【CP932 と誤認して文字化けする】
・BOM 付き UTF-8 なら
   → 正しく UTF-8 と認識される
// Web アプリから CSV をダウンロードさせる際
var bom = new UTF8Encoding(true).GetPreamble();  // EF BB BF
await stream.WriteAsync(bom);
await using var writer = new StreamWriter(stream, new UTF8Encoding(false));

XML では BOM を付けないのが無難、CSV では BOM を付ける——
という逆の指針になる点に注意する。

現況の補足:

項目 現在
Excel の「保存」 既定は今も ANSI(CP932)。「CSV UTF-8」形式が別に選べる
Microsoft 365 **「CSV UTF-8 (コンマ区切り)」**が選択肢にある(BOM 付き)
区切り文字 地域設定に依存(欧州では ; になる国がある)
改行を含むセル " で囲む。RFC 4180

そもそも CSV を避けられるなら避けるのが最善である。

代替 利点
.xlsx を直接生成(ClosedXML 等) 文字コードの問題が消える。書式も付けられる
JSON 構造を持てる。UTF-8 が前提
TSV 区切り文字の衝突が起きにくい

CSV は**「区切り文字」「引用符」「改行」「文字コード」の 4 つが
すべて曖昧**という、交換形式として弱い仕様である。
帳票出力 で触れた通り、
利用者が Excel で加工する前提なら .xlsx を直接吐く方が確実である。

参考

Microsoft Learn


Tags: 移行, .NET開発, 国際化対応, 文字コード

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally