パスワードを忘れた? アカウント作成
1813730 journal
プログラミング

Yoh2の日記: [C/C++規格: 12] C11の新機能その3 -- ユニコード文字・文字列 2

日記 by Yoh2

新機能一覧の概要はこちら→ [C/C++規格: 10] C11の新機能 その1 -- 新機能概要

[2011-04-01変更: char16_t、char32_tのエンコーディングについてコメントを頂いたので修正]

あかん。一ヶ月以上空いてしまった。
次はメモリ順序化のモデルとアトミック型について書こうと思ってたけどまだ読めてない><。
というわけでお茶濁しながら別の話題で。

今回紹介するのはユニコード文字・文字列。
これらはC++11で一歩早く採用されています。wchar_tの場合と同様に微妙な違いもありますが。
C++11の方もまだ見れていないので具体的な違いを全部挙げることはできませんが、例えば新しい型char16_t、char32_tが、C++では独立した型なのに対し、Cでは他の型のtypedefだとか。

■ 新しい文字型
<uchar.h>をインクルードすることで、以下の型が定義される。

  • char16_t : 16ビット文字型。のuint_least_16_tと同じ型であると定義される。
  • char32_t : 32ビット文字型。のunit_least_32_tと同じ型であると定義される。

[2011-04-01変更: ここから]
処理系によってマクロ__STDC_UTF_16__、__STDC_UTF_32__が1に定義されていれば、それぞれchar16_tがUTF-16エンコーディング、char32_tがUTF-32エンコーディングされた文字を扱う。
これらのマクロが定義されていなければどんなエンコーディングかは処理系定義。
以下、char16_tがUTF-16エンコーディング、char32_tがUTF-32エンコーディングであると仮定して解説しています。違う場合もあり得るので訂正すべきなんだろうけど変な訂正ミスをやらかしそうなので(汗

それぞれUTF-16エンコーディング、UTF-32エンコーディングされた文字列を扱う型。
……という理解をしてたけど、改めて規格書を見直してみると明記されていないような(汗)
とりあえず、この理解が正しいという前提で解説します。間違ってたらゴメン。
# 後述する文字定数、文字列リテラルの接頭子が「u, U」でUTF-*じゃないとかあり得ないでしょ。
# さらに文字列リテラルの接頭子「u8」はUTF-8だということは明記されてるし。
[2011-04-01変更: ここまで]

■ 文字定数
書式 (括弧内に書かれた型は、文字並びの部分が一文字だけの場合):

  '文字並び' (int型)
  L'文字並び' (wchar_t型に対応する型なし整数型……ややこしいな)
  u'文字並び' (char16_t型)
  U'文字並び' (char32_t型)

最初のふたつはl旧Cでもお馴染のもの。残りふたつがC11で新たに定義された文字定数。
それぞれUTF-16エンコーディング、UTF-32エンコーディングの文字となる。整数リテラルの接尾子と違い、uが大文字か小文字かで意味が異なることに注意。

また、シングルクォートの間に複数文字を書くことができるが、そうした場合、どういった値になるかは旧Cと同様に処理系定義。新しい接頭子uやUが付いた文字定数の場合も同様。

■ 文字列リテラル
書式:

  "文字並び" (char配列型)
  L"文字並び" (wchar_t配列型)
  u8"文字並び" (char配列型)
  u"文字並び" (char16_t配列型)
  U"文字並び" (char32_t配列型)

これまた最初のふたつはお馴染のもの。最後のふたつは文字定数と同様に、それぞれUTF-16エンコーディング、UTF-32エンコーディングの文字列を表す。
文字列リテラルには、さらに接頭子 u8 で始まる文字列も追加された。
これは単なる文字列と同様にchar配列型だが、UTF-8エンコーディングされたもの。
通常の文字列がShift_JISだろうがEBCDICだろうが、u8"〜"と書くとその内容はUTF-8エンコーディングされたバイト列になることが保証される。

■ 文字列の連結
C99までは、文字列リテラルを複数並べるとそれをひとつの文字列リテラルに連結するといった機能があった。
C11でもこの機能は健在だが、文字列リテラルの種類が増えたため規則が増えた。
複数の文字列リテラルを並べて書くと、最終的な文字列は以下のようになる。

  • すべての文字列が接頭子なし → 通常の(ワイドじゃない)文字列 (今まで通り)
  • 接頭子(L, u8, u, U)が付いているものがある。出現する接頭子は一種類 → その接頭子に対応する文字列 (今まで通り + α)
  • ワイド文字列の接頭子(L, u, U)が付いているものがある。複数種類の接頭子が混在 → どのワイド文字列になるかは処理系定義 (新ルール)
  • ワイド文字列の接頭子(L, u, U)が付いているものとUTF-8文字列の接頭子(u8)が付いているものが混在している。 → コンパイルエラー。 (新ルール)

とりあえずは混ぜるな危険、と覚えておけばトラブルにはならないと思う。

■ 追加関数
<uchar.h>をインクルードすることで使えるようになる関数。

size_t mbrtoc16(char16_t *restrict pc16, const char *restrict s, size_t n, mbstate_t *restrict ps);
size_t mbrtoc32(char32_t *restrict pc32, const char *restrict s, size_t n, mbstate_t *restrict ps);

それぞれ、<wchar.h>をインクルードすることで使えるようになる関数 mbrtowc()のchar16_t、char32_t版。
マルチバイト文字を表す文字列をchar16_t文字1文字またはchar32_t文字1文字に変換する。

size_t c16rtomb(char *restrict s, char16_t c16, mbstate_t restrict ps);
size_t c32rtomb(char *restrict s, char32_t c32, mbstate_t restrict ps);

それぞれ、<wchar.h>をインクルードすることで使えるようになる関数 wcrtomb()のchar16_t、char32_t版。
char16_t文字1文字またはchar32_t文字1文字をマルチバイト文字1文字を表す文字列に変換する。

UTF-8文字とマルチバイト文字の相互変換関数は見当たらないが、UTF-8文字とUTF-16文字またはUTF-32文字との変換は簡単なので、必要ならプログラムを作ればよいということか?

# このペースだと、私が全部規格に目を通す前にもっといいまとめサイトとか
# 書籍とか出てきそうだな。
# や、それはそれで大歓迎なんだけど。楽できるし(笑

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by Egtra (38265) on 2012年03月29日 21時39分 (#2126367)

    char16_t/char32_tがUTF-16/UTF-32の場合、__STDC_UTF_16__/__STDC_UTF_32__が定義されるという規定がC11にあるはずです(N1570の6.10.8.2 Environment macrosより)。

    • 規格書を見直してみるとありました。ご指摘ありがとうございます。
      こんな定義ですね。

      __STDC_UTF_16__が1に定義されている: char16_t はUTF-16エンコーディング
      __STDC_UTF_16__が定義されていない: char16_tは処理系定義

      __STDC_UTF_32__ もUTF-32について同様。

      ※ C11では1に定義されているか全く定義されないかの二者択一。2に定義される等のパターンはない。

      # char16_t, char32_tの定義自体があるかないかを表すマクロがあるものとばかり思ってました。

      --
      巧妙に潜伏したバグは心霊現象と区別が付かない。
      親コメント
typodupeerror

身近な人の偉大さは半減する -- あるアレゲ人

読み込み中...