Unicodeの仕様に基づいた大文字小文字を変換するには

アルファベット26文字以外の大文字小文字の変換ルールは意外と複雑で、以下は言語ごとにどう変換してくれるかの比較。

Unicodeの大文字小文字の変換ルールに関しては Unicodeの大文字と小文字のまとめも参照。

試した言語の中ではPythonが一番優秀のようだ。

Scala

Scalaでの例

println("heLLO");
// => heLLO

// 文字列中のすべての文字を小文字にする
println("heLLO".toLowerCase);
// => hello

// 文字列の先頭の文字を大文字にし、2文字目以降はそのままにする
// 2文字目以降を小文字にすることはしない
println("heLLO".capitalize);
// => HeLLO

// 文字列中のすべての文字を大文字にする
println("heLLO".toUpperCase);
// => HELLO

println("\u00E6\u00E6\u00E6");
// => æææ

// 7ビットを超えるUnicodeの文字も正しく変換する
println("\u00E6\u00E6\u00E6".capitalize);
// => Æææ

// 7ビットを超えるUnicodeの文字も正しく変換する
println("\u00E6\u00E6\u00E6".toUpperCase);
// => ÆÆÆ

println("\u00DF");
// => ß

// "Ss" が期待されるが、変換しない
println("\u00DF\u00DF\u00DF".capitalize);
// => ß

// 大文字への変換であれば、2文字以上への変換もする
println("\u00DF".toUpperCase);
// => SS

println("\uFB01ght");
// => fight

println("\uFB01ght".toLowerCase);
// => fight

// "Fight" が期待されるが、なぜかこれは変換しない
println("\uFB01ght".capitalize);
// => fight

// 大文字への変換であれば、リガチャーも分解して変換する
println("\uFB01ght".toUpperCase);
// => FIGHT

println("\u03A3\u03A3\u03A3");
// => ΣΣΣ

// ギリシャ文字シグマ小文字は単語の最後では形が違うが、正しく変換する
println("\u03A3\u03A3\u03A3".toLowerCase);
// => σσς

// 1文字の場合は語末形にはならない
println("\u03A3".toLowerCase);
// => σ

// 1文字目はハンガリー語で1文字として扱われる"dz"
println("\u01F3s\u00FAsz");
// => dzsúsz

println("\u01F3s\u00FAsz".toLowerCase);
// => dzsúsz

// "Dzsúsz" が期待されるが、"dz"全体を大文字にしてしまい、正しく変換しない
println("\u01F3s\u00FAsz".capitalize);
// => DZsúsz

// 大文字への変換であれば、正しく変換する
println("\u01F3s\u00FAsz".toUpperCase);
// => DZSÚSZ

これは以下の環境での実行例である。

$ java -version
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)
$ scala -version
Scala code runner version 2.10.2 -- Copyright 2002-2013, LAMP/EPFL

PHP

$str = "heLLO";
echo $str . "\n";
// => heLLO
echo mb_convert_case($str, MB_CASE_LOWER, "UTF-8") . "\n";
// => hello
echo mb_convert_case($str, MB_CASE_TITLE, "UTF-8") . "\n";
// => Hello
echo mb_convert_case($str, MB_CASE_UPPER, "UTF-8") . "\n";
// => HELLO

$str = "\xC3\xA6";
echo $str . "\n";
// =>  æ
echo mb_convert_case($str, MB_CASE_LOWER, "UTF-8") . "\n";
// =>  æ
echo mb_convert_case($str, MB_CASE_TITLE, "UTF-8") . "\n";
// =>  Æ
echo mb_convert_case($str, MB_CASE_UPPER, "UTF-8") . "\n";
// =>  Æ

$str = "\xC3\x9F";
echo $str . "\n";
// =>  ß
echo mb_convert_case($str, MB_CASE_LOWER, "UTF-8") . "\n";
// =>  ß
echo mb_convert_case($str, MB_CASE_TITLE, "UTF-8") . "\n";
// =>  ß
echo mb_convert_case($str, MB_CASE_UPPER, "UTF-8") . "\n";
// =>  ß

$str = "\xEF\xAC\x81ght";
echo $str . "\n";
// => fight
echo mb_convert_case($str, MB_CASE_LOWER, "UTF-8") . "\n";
// => fight
echo mb_convert_case($str, MB_CASE_TITLE, "UTF-8") . "\n";
// => fight
echo mb_convert_case($str, MB_CASE_UPPER, "UTF-8") . "\n";
// => fiGHT

$str = "\xCE\xA3\xCE\xA3\xCE\xA3";
echo $str . "\n";
// => ΣΣΣ
echo mb_convert_case($str, MB_CASE_LOWER, "UTF-8") . "\n";
// => σσσ
echo mb_convert_case($str, MB_CASE_TITLE, "UTF-8") . "\n";
// => Σσσ
echo mb_convert_case($str, MB_CASE_UPPER, "UTF-8") . "\n";
// => ΣΣΣ

$str = "\xC7\xB3s\xC3\xBAsz";
echo $str . "\n";
// => dzsúsz
echo mb_convert_case($str, MB_CASE_LOWER, "UTF-8") . "\n";
// => dzsúsz
echo mb_convert_case($str, MB_CASE_TITLE, "UTF-8") . "\n";
// => Dzsúsz
echo mb_convert_case($str, MB_CASE_UPPER, "UTF-8") . "\n";
// => DzSÚSZ

上記実行例のPHPのバージョンは5.5.3。

Python

Python3での例

str = "heLLO"
print(str);
# => heLLO

# 文字列中のすべての文字を小文字にする
print(str.lower());
# => hello

# 文字列の先頭の文字を大文字にし、2文字目以降を小文字にする
print(str.title());
# => Hello

# 文字列中のすべての文字を大文字にする
print(str.upper());
# => HELLO

str = "\u00E6\u00E6\u00E6"
print(str);
# => æææ

print(str.lower());
# => æææ

# 7ビットを超えるUnicodeの文字も正しく変換する
print(str.title());
# => Æææ

# 7ビットを超えるUnicodeの文字も正しく変換する
print(str.upper());
# => ÆÆÆ

str = "\u00DF" # ß
print(str);
# => ß

print(str.lower());
# => ß

# 1文字から2文字への変換もする
print(str.title());
# => Ss

# 1文字から2文字への変換もする
print(str.upper());
# => SS

str = "\uFB01ght"
print(str);
# => fight

print(str.lower());
# => fight

# リガチャも2文字に分解した上で、1文字目のみを大文字に変換する
print(str.title());
# => Fight

# 全部大文字にする場合もリガチャを分解する
print(str.upper());
# => FIGHT

str = "\u03A3\u03A3\u03A3"
print(str);
# => ΣΣΣ

# ギリシャ文字シグマ小文字は単語の最後では形が違うが、正しく変換する
print(str.lower());
# => σσς

# 1文字の場合は語末形にはならない
str = "\u03A3"
print(str.lower());
# => σ

# 1文字目はハンガリー語で1文字として扱われる"dz"
str = "\u01F3s\u00FAsz"
print(str);
# => dzsúsz

print(str.lower());
# => dzsúsz

# 本当の1文字目のみを大文字に変換する
print(str.title());
# => Dzsúsz

# 全体を大文字に変換する
print(str.upper());
# => DZSÚSZ

JavaScript

1文字目だけを大文字にするための便利なメソッドはないようだ。

var str = "heLLO";
console.log(str);
// => heLLO

// 文字列中のすべての文字を小文字にする
console.log(str.toLowerCase());
// => hello

// 文字列中のすべての文字を大文字にする
console.log(str.toUpperCase());
// => HELLO

str = "\u00E6\u00E6\u00E6";
console.log(str);
// => æææ

// 7ビットを超えるUnicodeの文字も正しく変換する
console.log(str.toUpperCase());
// => ÆÆÆ

str = "\u00DF";
console.log(str);
// => ß

// 大文字への変換であれば、2文字以上への変換もする
console.log(str.toUpperCase());
// => SS
// Firefoxでは ß のままになってしまう

str = "\uFB01ght";
console.log(str);
// => fight

console.log(str.toLowerCase());
// => fight

// 大文字への変換であれば、リガチャーも分解して変換する
console.log(str.toUpperCase());
// => FIGHT

str = "\u03A3\u03A3\u03A3";
console.log(str);
// => ΣΣΣ

// ギリシャ文字シグマ小文字は単語の最後では形が違うが、正しく変換する
console.log(str.toLowerCase());
// => σσς
// Firefoxでは語末形にしてくれず3文字とも同じになってしまう

str = "\u03A3";
// 1文字の場合も語末形にはなる
console.log(str.toLowerCase());
// => ς
// Firefoxでは語末形にしてくれない

str = "\u01F3s\u00FAsz";
// 1文字目はハンガリー語で1文字として扱われる"dz"
console.log(str);
// => dzsúsz

console.log(str.toLowerCase());
// => dzsúsz

console.log(str.toUpperCase());
// => DZSÚSZ

上記実行例は nodejs v0.10.15, Chrome 40, Firefox 32 で確認。

このサイトは筆者(hydrocul)の個人メモの集合です。すべてのページは永遠に未完成です。