0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

文字コードとバイト数を完全整理|UTF-8・Shift-JIS・半角カナの落とし穴まで

0
Posted at

はじめに

「なぜ日本語は2バイト?」「半角カナってSJISとUTF-8で違うの?」「ファイルを開いたら文字化けした」──こうした疑問の根本は 文字コードとバイト数の関係 にあります。

本記事では、主要な文字コード規格ごとのバイト数を体系的に整理し、ファイルエンコーディングにまつわる実践的な知識もまとめます。


1. 文字コードの基本

「文字集合」と「符号化方式」の違い

用語 意味
文字集合(Character Set) どの文字を収録するかの規格 Unicode、JIS X 0208
符号化方式(Encoding) 文字をどのバイト列に変換するかの規格 UTF-8、Shift-JIS

Unicode という文字集合を、UTF-8・UTF-16・UTF-32 という異なる符号化方式でバイト列に変換できます。


2. 主要エンコーディングとバイト数

2-1. ASCII(US-ASCII)

  • バイト長:固定 1 バイト
  • 収録文字:英数字・記号・制御文字(128文字)
  • コードポイント範囲:U+0000〜U+007F(0x00〜0x7F)
'A' → 0x41 (1バイト)
'0' → 0x30 (1バイト)
' ' → 0x20 (1バイト)

2-2. UTF-8

Unicode文字を可変長(1〜4バイト)で表現します。ASCIIと後方互換性があり、現在のデファクトスタンダードです。

コードポイント範囲 バイト数 代表的な文字
U+0000〜U+007F 1バイト 半角英数字・記号(ASCII互換)
U+0080〜U+07FF 2バイト ラテン文字拡張、アラビア文字など
U+0800〜U+FFFF 3バイト ひらがな・カタカナ・漢字・半角カナ
U+10000〜U+10FFFF 4バイト 絵文字・一部の漢字(CJK拡張)など
# Python で確認
text = "A あ 亜 😀 ア"
for c in text.split():
    b = c.encode("utf-8")
    print(f"{c!r}{b.hex()} ({len(b)}バイト)")

実行結果:

'A' → 41 (1バイト)
'あ' → e3818 (3バイト)    # U+3042
'亜' → e4ba9c (3バイト)   # U+4E9C
'😀' → f09f9880 (4バイト) # U+1F600
'ア' → efbdb1 (3バイト)    # U+FF71(半角カタカナ)

ポイント:UTF-8 では半角カナも 3バイト です。「半角だから軽い」とはなりません。


2-3. Shift-JIS(SJIS / CP932)

Windowsや古い日本語システムで広く使われてきたエンコーディングです。

文字の種類 バイト数
半角英数字・記号(ASCII範囲) 1バイト A0!
半角カタカナ 1バイト (0xA1〜0xDF)
ひらがな・カタカナ(全角) 2バイト
漢字(JIS第1〜2水準) 2バイト
text = "A あ 亜 ア"
for c in text.split():
    try:
        b = c.encode("shift_jis")
        print(f"{c!r}{b.hex()} ({len(b)}バイト)")
    except UnicodeEncodeError:
        print(f"{c!r} → エンコード不可")

実行結果:

'A' → 41 (1バイト)
'あ' → 82a0 (2バイト)
'亜' → 88ad (2バイト)
'ア' → b1 (1バイト)   ← 半角カナは1バイト!

ポイント:Shift-JIS では半角カナが 1バイト。UTF-8(3バイト)と大きく異なります。


2-4. EUC-JP

主にUnix/Linux系で使われた日本語エンコーディング。

文字の種類 バイト数
半角英数字(ASCII範囲) 1バイト
ひらがな・カタカナ・漢字 2バイト
半角カタカナ 2バイト(0x8E + 1バイト)
JIS第3・4水準漢字 3バイト

2-5. UTF-16

文字の種類 バイト数
BMP内の文字(U+0000〜U+FFFF) 2バイト
サロゲートペア(U+10000〜) 4バイト

JavaやC#の内部文字列表現はUTF-16です。String.length() が返す値は「文字数」ではなく「UTF-16コードユニット数」のため、絵文字を含む文字列では注意が必要です。

String s = "😀";
System.out.println(s.length());         // 2(サロゲートペア)
System.out.println(s.codePointCount(0, s.length())); // 1(文字数)

3. 文字種別・エンコード別バイト数まとめ

文字 文字種 UTF-8 Shift-JIS EUC-JP UTF-16
A 半角英字 1 1 1 2
1 半角数字 1 1 1 2
(全角) 全角記号 3 2 2 2
ひらがな 3 2 2 2
全角カタカナ 3 2 2 2
半角カタカナ 3 1 2 2
漢字(基本) 3 2 2 2
𠮷(吉の異体字) 漢字(拡張) 4 エラー エラー 4
😀 絵文字 4 エラー エラー 4

UTF-8 で表現できない文字は Shift-JIS / EUC-JP では「エンコード不可」となります。


4. 半角カナの落とし穴

なぜ半角カナは問題になるか

観点 問題
UTF-8では3バイト 「半角だから容量節約」にならない
文字化けリスク SJISとUTF-8が混在するシステムで化ける
検索・照合の不一致 DBの照合順序によって全角と区別される
NFKC正規化で全角変換 Unicodeの正規化で意図せず全角になることがある

実務での推奨

  • 新規システム:半角カナを使わない(全角カタカナで統一)
  • 既存データの変換: unicodedata.normalize('NFKC', text) で全角変換
import unicodedata

text = "アイウエオ"
normalized = unicodedata.normalize("NFKC", text)
print(normalized)  # アイウエオ(全角カタカナ)

5. ファイルエンコーディングの実践知識

5-1. BOM(Byte Order Mark)

UTF-8、UTF-16 などのファイル先頭に付与されるマーカーバイト。

エンコーディング BOMバイト列
UTF-8 with BOM EF BB BF
UTF-16 LE FF FE
UTF-16 BE FE FF

UTF-8のBOMは不要(UTF-8にバイト順はない)ですが、Excelなどが自動付与することがあります。BOM付きUTF-8をそのまま読み込むとプログラムで先頭に \ufeff が混入するトラブルがあります。

# BOM付きファイルを安全に読む
with open("file.csv", encoding="utf-8-sig") as f:  # utf-8-sig でBOMを自動除去
    content = f.read()

5-2. 改行コード

改行コード バイト 使用OS
LF(\n 0x0A Unix / Linux / macOS
CRLF(\r\n 0x0D 0x0A Windows
CR(\r 0x0D 旧macOS(〜OS9)

テキストをバイナリモードで読む場合は改行コードが変換されないため注意が必要です。Gitリポジトリでは .gitattributestext=auto を設定することで自動変換できます。

* text=auto
*.sh text eol=lf
*.bat text eol=crlf

5-3. 文字化けの原因と対処

原因 症状 対処
書き込みと読み込みのエンコードが違う ひらがなが ???繝「繝? になる 両端のエンコードを揃える
BOM付きUTF-8を通常UTF-8として読む 先頭に  が混入 utf-8-sig で読む
Shift-JISファイルをUTF-8として読む 縺ゅk のような文字列 正しいエンコードを指定
サロゲートペア非対応のシステム 絵文字や一部漢字が ? になる UTF-8対応に移行

文字化けしたファイルのエンコード推測:

import chardet

with open("unknown.txt", "rb") as f:
    raw = f.read()
result = chardet.detect(raw)
print(result)  # {'encoding': 'SHIFT_JIS', 'confidence': 0.99, 'language': 'Japanese'}

5-4. データベースの文字コード設定

MySQLの utf8実は3バイト制限(4バイト文字=絵文字・一部漢字を格納できない)です。絵文字を扱うなら utf8mb4 を使います。

-- テーブル作成時
CREATE TABLE messages (
    id INT PRIMARY KEY,
    body TEXT
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 既存テーブルの変更
ALTER TABLE messages CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

5-5. プログラミング言語での文字列長

「文字数」の定義はエンコーディングや言語によって異なります。

# Python 3(str はUnicodeコードポイント単位)
s = "😀"
print(len(s))              # 1(コードポイント数)
print(len(s.encode("utf-8")))  # 4(バイト数)
// Java(String はUTF-16コードユニット単位)
String s = "😀";
System.out.println(s.length());         // 2(UTF-16コードユニット)
System.out.println(s.codePointCount(0, s.length())); // 1(コードポイント数)
System.out.println(s.getBytes("UTF-8").length);      // 4(バイト数)
// JavaScript(String はUTF-16コードユニット単位)
const s = "😀";
console.log(s.length);              // 2
console.log([...s].length);         // 1(スプレッド構文でコードポイント単位)
console.log(new TextEncoder().encode(s).length); // 4(バイト数)

6. 演習問題

⭐ 問題1:バイト数を計算せよ

次の文字列を UTF-8 でエンコードした場合のバイト数を答えてください。

"Hello世界"
模範解答
  • H, e, l, l, o → 各1バイト × 5 = 5バイト
  • → U+4E16(BMP内) → 3バイト
  • → U+754C(BMP内) → 3バイト
  • 合計:5 + 3 + 3 = 11バイト
print(len("Hello世界".encode("utf-8")))  # 11

⭐⭐ 問題2:半角カナの差を確認せよ

次のコードの出力を答えてください。

s = "アイウ"
print(len(s.encode("utf-8")))
print(len(s.encode("shift_jis")))
模範解答
9   # UTF-8:半角カナは3バイト × 3文字
3   # Shift-JIS:半角カナは1バイト × 3文字

半角カタカナ()は UTF-8 では U+FF65〜U+FF9F 範囲のBMP文字として3バイトで表現されます。Shift-JIS では 0xA1〜0xDF の1バイト領域に割り当てられています。


⭐⭐⭐ 問題3:エンコード推測と変換

Shift-JIS で書かれた次のバイト列を UTF-8 の文字列に変換してください。

raw = bytes([0x82, 0xA0, 0x82, 0xA2, 0x82, 0xA4])  # SJISの「あいう」
模範解答
raw = bytes([0x82, 0xA0, 0x82, 0xA2, 0x82, 0xA4])
text = raw.decode("shift_jis")   # SJISとして復号 → 'あいう'
utf8_bytes = text.encode("utf-8")  # UTF-8に再エンコード
print(text)         # あいう
print(utf8_bytes)   # b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86'
print(len(utf8_bytes))  # 9(3バイト × 3文字)

まとめ

エンコーディング 半角英数 ひらがな・漢字 半角カナ 絵文字
UTF-8 1B 3B 3B 4B
Shift-JIS 1B 2B 1B 非対応
EUC-JP 1B 2B 2B 非対応
UTF-16 2B 2B 2B 4B
  • 新規開発は UTF-8(BOMなし)一択
  • 半角カナは使わない(文字化けリスク+UTF-8では軽くない)
  • MySQLは utf8 でなく utf8mb4 を使う
  • 絵文字や拡張漢字は4バイトlength() 系APIは「バイト数」「UTF-16コードユニット数」「コードポイント数」のどれを返すか意識する

参考


@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?