将棋プログラミング

(将棋に関する)ソフトウェア開発のノウハウ等。

UTF-8 の BOM について

1.はじめに

UTF-8文字コードのファイルには、BOM (Byte Order Mark) がある場合とない場合がある。

Unicode の規格では、BOM は、推奨されないが、許容されている。

ja.wikipedia.org

今回、必要があり、色々な OS や言語で、UTF-8文字コードのファイルを作成した時、BOM が記録されるか、されないか、を調べた。

2.色々な OS や言語での BOM

2.1 Windows 10, Visual Studio, C++, _wfopen (_tfopen)

// Visual Studio 2005 以降 保存
FILE *fp = _wfopen(name, _ L"w, ccs=UTF-8");
if  (fp == NULL)  {
   // エラー処理
}
fwprintf_s(fp, L"ABC漢字123\n");
fclose(fp);

// 読み込み
FILE *fp = _wfopen(name L"r, ccs=UTF-8");

保存では、 BOM付になる。

learn.microsoft.com

マイクロソフトのドキュメントでも、次のように明記されている。

Unicode モードで書き込むように開かれたファイルには、自動的に BOM が書き込まれます。


読み込みは、BOM があっても無くても問題はない。
BOM によって文字コードの自動判定まで行っている。

2.2 Windows 10, Visual Studio, C++, _wfopen (_tfopen), BOM 無しにする方法

_wfopen() を使い、BOM 無しにするには、次の二つの方法がある。

(1) fseek() でBOM を上書き
FILE *fp = _wfopen(name, _L"w, ccs=UTF-8");
if (fp == NULL) {
   // エラー処理
}
// BOM (EF BB BF) を上書き
fseek(fp, 0L, SEEK_SET);
fwprintf_s(fp, L"ABC漢字123\n");
fclose(fp);
(2) バイナリで保存
FILE *fp = _wfopen(name, _L"wb");

2.3 Windows 10, Visual Studio, C++, MFC

CStdioFile file(_tfopen( name, L"w, ccs=UTF-8"));
file.WriteString(L"ABC☖☗漢字123\n");
file.Close();

保存では、 BOM付になる。
読み込みは、BOM があっても無くても問題はない。

2.4 Windows 10, Visual Studio, C++, Stream

std::wofstream stream("bom_test_15-2.txt");
const std::codecvt_utf8<wchar_t> * converter = new std::codecvt_utf8<wchar_t>;
const std::locale utf8_locale = std::locale(std::locale::empty(), converter);
stream.imbue(utf8_locale);
if (! stream.bad()) {
    std::wstring str = L"ABC☖☗漢字123\n";
    stream << str;
    stream.close();
}

保存では、BOM無しになる。

読み込みは、BOM があっても無くても問題はない。

2.5 Windows 10, Visual Studio, C#

Encoding enc = Encoding.GetEncoding("utf-8");
StreamWriter writer = new StreamWriter(@"D:\ファイル名", false, enc);
writer.WriteLine("ABC☖☗漢字123");
writer.Close();

保存では、 BOM付になる。

StreamReader sr = new StreamReader(name, Encoding.GetEncoding("utf-8"));
string str = sr.ReadToEnd();
sr.Close();

読み込みは、BOM があっても無くても問題はない。

2.6 Android OS, Java

// 保存
String fileName = “android_bom_test.txt";
String str = "ABC☖☗漢字123\n";
OutputStream out = null;
try {
    out = context.openFileOutput(fileName, Context.MODE_PRIVATE);
    out.write(str.getBytes());
    out.close();
} catch (Exception e) {
}

保存では、 BOMなしになる。

// 読み込み
FileReader reader = new FileReader(path);
char[] buffer = new char[BUF_SIZE];
reader.read(buffer);
String str = new String(buffer);
reader.close();

読み込みは、BOM があっても無くても問題はない。

2.7 macOS / iOS, Objective-C

// 保存
NSString *str = @"ABC☖☗漢字123\n"; 
NSError *error = nil;
BOOL result = [str writeToFile: file_name atomically: YES 	
	encoding: NSUTF8StringEncoding error: &error];

保存では、 BOMなしになる。

// 読み込み
NSError *error = nil;
NSString *str = [NSString stringWithContentsOfFile: file_name
                           encoding: NSUTF8StringEncoding error: &error ];

読み込みは、BOM があっても無くても問題はない。

2.8 Perl

!/usr/bin/env perl
use utf8;
use Encode;

open(file, "> $file_name");
print file Encode::encode( 'UTF-8', "ABC☖☗漢字123\n" );
close(file);

保存では、 BOMなしになる。

open(rfile, "<:utf8", $file_name);

読み込みは、BOM があっても無くても問題はない。

2.9 PHP

<?php
$file_name = "php_bom_test.txt";
file_put_contents($file_name, "ABC☖☗漢字123\n");
// 保存では、 BOMなしになる。

$str = file_get_contents($file_name);
print ("str=".$str."\n");
// 読み込みは、BOMがあっても無くても問題はない。
?>
PHP のテスト

2.10 JavaScript

const save = document.createElement('a');
save.href = URL.createObjectURL(new Blob(["ABC☖☗漢字123\n"], {type: 'text/plain'}));
save.download = 'JavaScriptTest.txt';	// file name
save.style.display = 'none';
document.body.appendChild(save);
save.click();

保存では、 BOMなしになる。

2.11 まとめ

以上をまとめると、次のようになる。

BOM まとめ

Windows で、Microsoft の開発ツールを使った場合、BOM が自動で記録される場合が多い。

・他の OS 等では、BOM が記録されない。

・読み込みは、BOMがあっても無くても問題はない。