特定の文字の文字コード(コード・ポイント)を取得する

Scala 3 (Dotty 0.26.0-RC1) 2.13.3 2.12.12
最終更新:2020年7月12日

[AD] scalapediaでは記事作成ボランティアを募集しています

この記事では、特定の文字の文字コード(コード・ポイント)を取得する方法について解説します。

文字がサロゲートペアかどうかで対応を変える

Javaにおける特定の文字の文字コードは、コード・ポイントと呼びます(つまり、Scalaでもそう呼びます)。

文字のコード・ポイントを取得する方法には、以下の二つがあります。

  1. String#codePointAtを使用する方法
  2. Char#toIntを使用する方法

通常の場合、つまり文字が「サロゲートペア」かもしれない場合には、 1. のString#codePointAtを使用します(※サロゲートペアについては後ほど説明します)。

文字がサロゲートペアではないことが保証されている場合には、 2. のChar#toIntを使用する方法で取得しても構いません。

サンプルとして以下の文字を用意しました。
どちらも「よし」ですが、ひとつめは一般的な「よし」、ふたつめはいわゆる「つちよし」です。
つちよしはサロゲートペアです。

これらの文字のコード・ポイントを取得してみましょう。

val character = "吉" val surrogateCharacter = "𠮷"

通常の場合は codePointAt​ メソッドを使う

通常の場合、つまり文字がサロゲートペアかもしれない場合にはString#codePointAtメソッドを使用しましょう。

java
public int codePointAt​(int index)

codePointAtメソッドは、指定したインデックスに位置する文字のコード・ポイントを返します。

codePointAtメソッドを使用したサンプルコードはこちらです。

println( character.codePointAt(0) ) println( surrogateCharacter.codePointAt(0) )

コード・ポイントを調べたい文字はいずれも文字列の1文字目なので、インデックスには0を指定しています。

実行結果は以下のようになります。

21513 134071

「よし」のコード・ポイント21513、「つちよし」のコード・ポイント134071が出力されました。

もしインデックスに指定した数値が文字列のインデックスのとりうる範囲を逸脱する場合、つまり負の数や文字列の長さ以上の値を指定した場合には、java.lang.IndexOutOfBoundsExceptionが投げられます。
範囲を逸脱しないよう注意してください。

サロゲートペアは Char 2個でString 1文字を表すイレギュラーな文字

ここで、後回しにしていた「サロゲートペア」についての説明をします。

そもそもサロゲートペアとは何でしょうか?

Javaの文字列の内部表現はUTF-16です。

サロゲートペアとは、UTF-16において「2文字分のデータ量で1文字を表す」ような文字のことをいいます。

UTF-16は、当初16ビットで1文字を表すこととしていました。
しかし16ビットで表現できる文字の数よりも表現したい文字の方が多かったため、この16ビットに入りきらなかった文字に関しては、2文字分の32ビットで表せるよう仕様が拡張されました。

この入りきらなかった文字こそがサロゲートペアです。

Javaにおいては、サロゲートペアはChar2個でString1文字を表すイレギュラーな文字として存在しています。

なぜサロゲートペアに注意しないといけないの?

とはいえ、内部表現の話がなぜ表に出てくるのか不思議に思う方もいると思います。

実はJavaの文字列の内部表現(Charの中身)は拡張前のUTF-16なのです。

つまりCharには16ビット、1文字分のデータしか入っていません。

1文字分のデータしか持っていないCharを使って拡張後のUTF-16に対応するための機能が別途用意されたわけです。

そして、その機能の一つがcodePointAtである、というわけです。

Javaでサロゲートペアの話が出るのはこういう理由があったわけです。

サロゲートペアを含まない場合は head​ toInt メソッドを組み合わせることもできる

さて、Javaの文字はそんな複雑な事情を抱えているのですが、サロゲートペアを含まない場合にはもう少し簡便にコード・ポイントを調べる方法があります。

まずStringCharに変換し、これをChar#toIntメソッドでChar内部のコード・ポイントを直接取得する方法です。

サンプルコードはこちらです。

println( character.head.toInt ) println( surrogateCharacter.head.toInt )

Stringの1文字目はheadメソッドで取得することができます。

戻り値はCharなので、toIntメソッドを使用すればコード・ポイントが取得できます。

実行結果は以下のようになります。

21513 55362

「よし」については、サロゲートペアではないので正しく取得できています。

「つちよし」はサロゲートペアなので、codePointAtメソッドの実行結果とは異なる値が出力されています。

これは2文字分のデータのうち、前方の1文字分である「上位サロゲート」が取得されてしまったためです。

この値はほんとうに「つちよし」ではないのでしょうか?調べてみましょう。

println( surrogateCharacter.head.toString )

Char#toStringメソッドを使用すると、そのコード・ポイントが実際に表す文字を取得することができます。

出力は以下のようになります。

?

?が出力されました。
つちよしの上位サロゲート55362に対応する文字が存在しないため、文字化けしてしまったということです。

サロゲートペアに対してはこの手法を使わないよう十分ご注意ください。

Scala Standard Library:scala.collection.StringOps#head
Scala Standard Library:scala.Char#toInt

まとめ

基本的には、とくにサロゲートペアを含む場合には codePointAt メソッドを使いましょう。

もしサロゲートペアを含まないことが保証されている場合には、headメソッドとtoIntメソッドを組み合わせることで同様の処理をすることができます。

備考:文字列を他の特定の文字セットに変換するには

「文字コード」と表記するとどうしてもASCIIやShift-JISやANSIなどを思い浮かべることが多いと思います。
Javaにおいてそれらは「文字セット」と呼んで区別されています。

また、Javaの文字列Stringや文字シーケンスCharSequence、文字Charなどの「文字コード」、すなわち内部表現に用いられる文字セットはUTF-16で固定されています。

文字列を他の特定の文字セットに変換したい場合は、以下の記事をご覧ください。

サイト内検索


カテゴリ「文字列処理」の記事

文字列をエスケープしたり復元したりする方法(Apache Commons Text) JavaとScalaのString/StringBuilder/StringBuffer使い分け事情 文字列を分割する方法(split・splitAt・linesIterator・linesWithSeparatorsメソッド) trimメソッドで文字列の前後の空白を除去する 文字列が一致するか比較する方法/大文字・小文字を区別せずに比較する方法 特定の文字の文字コード(コード・ポイント)を取得する replaceメソッドなど、文字列を置換する方法を紹介 文字列の先頭や末尾を、取得したり切り落としたりする方法 containsメソッドでStringに特定の文字列が含まれるか調べる方法 数値を文字列に変換する方法 stripメソッドで文字列の前後の全角空白を除去する 文字列を数値に変換するには?to○○メソッドと注意点について 文字列を辞書的に比較する方法/大文字・小文字を区別せずに比較する方法 文字列が特定の文字列で始まるか・終わるかを調べる方法 【getBytes&size】文字列のバイト長を取得する方法 substringでStringを切り取り、部分文字列を抽出する方法 StringOpsとWrappedStringの違いは? 文字列を連結するには?+演算子やString interpolationの使い方 文字列の大文字へ・小文字へ変換する方法 文字列を逆順にする方法 文字列の文字コード(文字セット)を変換する方法 文字列の長さを取得する方法:lengthとcodePointCountの使い分け 文字列をURLエンコード・デコードする方法 文字列が正規表現に合致するか調べる方法 この文字の位置はどこ?文字列のインデックスを取得する方法 文字列をバイト列に、またはバイト列を文字列に変換する方法

カテゴリ「文字列処理」の記事

文字列をエスケープしたり復元したりする方法(Apache Commons Text) JavaとScalaのString/StringBuilder/StringBuffer使い分け事情 文字列を分割する方法(split・splitAt・linesIterator・linesWithSeparatorsメソッド) trimメソッドで文字列の前後の空白を除去する 文字列が一致するか比較する方法/大文字・小文字を区別せずに比較する方法 特定の文字の文字コード(コード・ポイント)を取得する replaceメソッドなど、文字列を置換する方法を紹介 文字列の先頭や末尾を、取得したり切り落としたりする方法 containsメソッドでStringに特定の文字列が含まれるか調べる方法 数値を文字列に変換する方法 stripメソッドで文字列の前後の全角空白を除去する 文字列を数値に変換するには?to○○メソッドと注意点について 文字列を辞書的に比較する方法/大文字・小文字を区別せずに比較する方法 文字列が特定の文字列で始まるか・終わるかを調べる方法 【getBytes&size】文字列のバイト長を取得する方法 substringでStringを切り取り、部分文字列を抽出する方法 StringOpsとWrappedStringの違いは? 文字列を連結するには?+演算子やString interpolationの使い方 文字列の大文字へ・小文字へ変換する方法 文字列を逆順にする方法 文字列の文字コード(文字セット)を変換する方法 文字列の長さを取得する方法:lengthとcodePointCountの使い分け 文字列をURLエンコード・デコードする方法 文字列が正規表現に合致するか調べる方法 この文字の位置はどこ?文字列のインデックスを取得する方法 文字列をバイト列に、またはバイト列を文字列に変換する方法