この記事について
この記事は.NETのEncoding APIとUTF-8仕様を確認し、PowerShellで文字数とバイト数の違いを観察する形で整理しています。検証ステータス:📘 公式仕様確認済み・PowerShell実機未確認
Shift_JIS系code pageの利用可否はPowerShell/.NET環境で差があるため、取得できない場合の処理も含めています。
「10文字だから10バイト」とは限りません。文字列は保存や通信の前にEncodingによってbyte列へ変換され、そのbyte数は文字とEncodingの組み合わせで決まります。
この違いは、固定長データ、CSV、API、古い業務システムとの連携で重要になります。
文字列とbyte列は別物
flowchart LR
A[文字列] --> B{Encoding}
B -->|UTF-8| C[UTF-8 byte列]
B -->|code page 932| D[Shift_JIS系 byte列]
C --> E[byte数]
D --> F[byte数]
PowerShell/.NETではGetByteCountで、そのEncodingが生成するbyte数を数えられます。
UTF-8で確認する
$texts = @(
'ABC',
'日本',
'A日本',
'😀'
)
$utf8 = [System.Text.Encoding]::UTF8
foreach ($text in $texts) {
[pscustomobject]@{
Text = $text
StringSize = $text.Length
UTF8Bytes = $utf8.GetByteCount($text)
}
}
ASCII範囲の文字と日本語、絵文字では、同じ「見た目の1文字」でもUTF-8のbyte数が異なります。
String.Lengthも「人間が見る文字数」と同じとは限らない
.NETのStringはUTF-16 code unitの列として扱われます。そのため、補助平面の文字などでは、見た目の1文字とString.Lengthが一致しないことがあります。
flowchart TB
A[見た目の文字] --> B[.NET String]
B --> C[UTF-16 code units]
B --> D[Encodingでbyte列へ]
C --> E[String.Length]
D --> F[GetByteCount]
E -. 同じ概念ではない .- F
ここを「文字数」と「byte数」の2種類だけで単純化しすぎないのが大切です。
Shift_JIS系code page 932と比較する
PowerShell 7 / .NETでは、legacy code pageを使うためにCodePagesEncodingProviderの登録が必要な環境があります。
try {
$sjis = [System.Text.Encoding]::GetEncoding(932)
}
catch {
[System.Text.Encoding]::RegisterProvider(
[System.Text.CodePagesEncodingProvider]::Instance
)
$sjis = [System.Text.Encoding]::GetEncoding(932)
}
$utf8 = [System.Text.Encoding]::UTF8
foreach ($text in @('ABC', '日本', 'A日本')) {
[pscustomobject]@{
Text = $text
UTF8Bytes = $utf8.GetByteCount($text)
SJISBytes = $sjis.GetByteCount($text)
}
}
同じ文字列でも、Encodingが変わればbyte列も変わります。
「Shift_JISなら日本語は2バイト」と決めつけない
code pageに含まれない文字や、single-byteで表現される文字もあります。さらに、Windowsのcode page 932と「Shift_JIS」という名称を完全に同一の仕様として雑に扱うと、細部で誤解が生まれます。 、PowerShell/.NETでcode page 932を取得して比較する実験として扱います。
固定長データでなぜ問題になるのか
古いシステム連携では、項目幅が「文字」ではなくbyte単位で定義されることがあります。
項目A: 10 bytes 項目B: 20 bytes 項目C: 8 bytes
文字列を単純にSubstringで切ってから保存すると、想定byte数を超える場合があります。
flowchart LR
A[入力文字列] --> B[文字数で切る]
B --> C[Encoding]
C --> D{指定byte幅以内?}
D -->|No| E[固定長レコードがずれる]
D -->|Yes| F[次の項目へ]
逆に、byte列を途中で乱暴に切ると、multi-byte文字の途中で分断する可能性があります。固定長データでは「どのEncodingで何byteか」を仕様として確認する必要があります。
BOMと改行は別に考える
Encoding.GetByteCount(string)は、その文字列をEncodingしたときのbyte数を計算します。
一方、実際のファイルサイズには次が加わる場合があります。
BOM
CRLF / LFなどの改行
区切り文字
ファイル全体のheader
「文字列本体のbyte数」と「保存したファイルのサイズ」を混同しません。
実ファイルでも確認する
文字列を保存した後、実際のファイルサイズも比較すると理解しやすくなります。
$text = '日本'
$path = '.\utf8-sample.txt'
[System.IO.File]::WriteAllText($path, $text, [System.Text.UTF8Encoding]::new($false))
$bytes = [System.IO.File]::ReadAllBytes($path)
[pscustomobject]@{
TextByteCount = [System.Text.Encoding]::UTF8.GetByteCount($text)
FileByteCount = $bytes.Length
}
ここではBOMなしのUTF-8を明示しているので、余計な条件を減らして比較できます。
まとめ
文字列はEncodingによってbyte列へ変換される
String.Lengthと見た目の文字数とbyte数は同じ概念ではない
UTF-8とcode page 932では同じ文字列でもbyte数が変わる
固定長データは文字数ではなく仕様上のEncodingとbyte幅を見る
BOMや改行を含むファイルサイズは、文字列本体のGetByteCountとは別に考える
公式情報・一次情報
Microsoft Learn — Encoding.GetByteCount
https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding.getbytecount
Microsoft Learn — Encoding.GetEncoding
https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding.getencoding
Microsoft Learn — CodePagesEncodingProvider
https://learn.microsoft.com/en-us/dotnet/api/system.text.codepagesencodingprovider
RFC 3629 — UTF-8
https://www.rfc-editor.org/rfc/rfc3629.html


コメント