C++でマルチバイト文字をUTF-16に変換:std::mbrtoc16の使い方と代替方法


std::mbrtoc16 関数は、マルチバイト文字列を UTF-16 文字表現に変換するために使用される C++ 標準ライブラリの関数です。UTF-16 は、Unicode に基づいた 16 ビットの文字エンコーディングであり、幅広い種類の文字を表現するために使用されます。

構文

size_t std::mbrtoc16(char16_t* dest, const char* src, size_t n, mbstate_t* state);

引数

  • state: マルチバイト文字列の変換状態を保持するオブジェクトへのポインタ。
  • n: src から検査できる最大バイト数。
  • src: マルチバイト文字列の開始位置を指すポインタ。
  • dest: 変換された UTF-16 文字が格納される場所を指すポインタ。

戻り値

  • -3: サロゲートペアの一部が検出された場合。
  • -2: 不完全なマルチバイト文字が検出されたが、n バイト以内に収まらない場合。
  • -1: エンコーディングエラーが発生した場合。
  • 0: src が NULL ポインタの場合、または変換された文字が NULL 文字の場合。
  • 成功した場合: 変換されたマルチバイト文字のバイト数。

動作

std::mbrtoc16 関数は、src から最大 n バイトを読み取り、dest に対応する UTF-16 文字を書き込みます。変換状態は state オブジェクトによって保持されます。

  • マルチバイト文字がサロゲートペアの一部の場合、関数は -3 を返し、state オブジェクトは次の呼び出しのために更新されます。次の呼び出しでは、dest にサロゲートペアの 2 番目の文字が書き込まれます。
  • マルチバイト文字が誤ったエンコーディングの場合、関数は -1 を返し、errnoEILSEQ エラーが設定されます。
  • マルチバイト文字が不完全な場合、関数は -2 を返し、state オブジェクトは次の呼び出しのために更新されます。
  • マルチバイト文字が完全な場合、その文字が dest に書き込まれ、関数はその文字のバイト数を返します。

#include <iostream>
#include <uchar>

int main() {
  char16_t dest[2];
  char src[] = "こんにちは";
  mbstate_t state = {};

  size_t n = std::mbrtoc16(dest, src, sizeof(src) - 1, &state);

  if (n == -1) {
    std::cerr << "エンコーディングエラー" << std::endl;
    return 1;
  }

  std::wcout << dest << std::endl;

  return 0;
}

この例では、std::mbrtoc16 関数を使用して、"こんにちは" というマルチバイト文字列を UTF-16 文字表現に変換し、ワイド文字出力ストリームに書き込んでいます。

  • std::mbrtoc16 関数は、サロゲートペアを処理できますが、複雑な処理が必要となる場合があります。サロゲートペアを処理する必要がある場合は、std::codecvt クラスを使用することを検討してください。
  • std::mbrtoc16 関数は、マルチバイト文字列のエンコーディングに依存します。エンコーディングが正しくないと、エンコーディングエラーが発生する可能性があります。


#include <iostream>
#include <uchar>

int main() {
  char16_t dest[2];
  char src[] = "こんにちは";
  mbstate_t state = {};

  size_t n = std::mbrtoc16(dest, src, sizeof(src) - 1, &state);

  if (n == -1) {
    std::cerr << "エンコーディングエラー" << std::endl;
    return 1;
  }

  std::wcout << dest << std::endl;

  return 0;
}

説明

例 2: マルチバイト文字列を UTF-16 文字表現に変換して文字列に格納する

#include <iostream>
#include <string>
#include <uchar>

int main() {
  std::string src = "こんにちは";
  std::u16string dest;

  mbstate_t state = {};
  const char* p = src.c_str();
  char16_t* q = &dest[0];

  while (true) {
    size_t n = std::mbrtoc16(q, p, src.size() - (p - src.c_str()), &state);

    if (n == 0) {
      break;
    } else if (n == -1) {
      std::cerr << "エンコーディングエラー" << std::endl;
      return 1;
    } else if (n == -2) {
      // 不完全なマルチバイト文字
      // 次回の呼び出しのために state を更新する
    } else if (n == -3) {
      // サロゲートペア
      // サロゲートペアの 2 番目の文字を書き込む
    } else {
      // 変換された文字を dest に書き込む
      q += n;
      p += n;
    }
  }

  std::cout << dest << std::endl;

  return 0;
}

説明

この例では、std::mbrtoc16 関数を使用して、"こんにちは" というマルチバイト文字列を UTF-16 文字表現に変換し、std::u16string オブジェクトに格納しています。

例 3: UTF-16 文字列をマルチバイト文字列に変換する

#include <iostream>
#include <string>
#include <uchar>

int main() {
  std::u16string src = u"こんにちは";
  std::string dest;

  mbstate_t state = {};
  char* p = &dest[0];
  const char16_t* q = &src[0];

  while (true) {
    size_t n = std::mbsrtoc16(p, q, dest.size() - (p - dest.c_str()), &state);

    if (n == 0) {
      break;
    } else if (n == -1) {
      std::cerr << "エンコーディングエラー" << std::endl;
      return 1;
    } else if (n == -2) {
      // 不完全なマルチバイト文字
      // 次回の呼び出しのために state を更新する
    } else {
      // 変換されたマルチバイト文字を dest に書き込む
      p += n;
      q += n;
    }
  }

  std::cout << dest << std::endl;

  return 0;
}

説明

この例では、std::mbsrtoc16 関数を使用して、u"こんにちは" という UTF-16 文字列をマルチバイト文字列に変換し、std::string オブジェクトに格納しています。



  • 1 バイトずつ処理するため、パフォーマンスが低下する可能性があります。
  • サロゲートペアの処理が複雑です。
  • マルチバイト文字列のエンコーディングに依存しており、エンコーディングが正しくないと、エンコーディングエラーが発生する可能性があります。

これらの制限を回避するために、std::mbrtoc16 関数の代替方法をいくつか検討することができます。

std::codecvt クラスを使用する

std::codecvt クラスは、文字エンコーディング間の変換を行うための汎用的なフレームワークを提供します。std::mbrtoc16 関数よりも柔軟性が高く、エンコーディングエラーやサロゲートペアの処理をより簡単に処理できます。

#include <iostream>
#include <locale>
#include <string>
#include <vector>

int main() {
  std::string src = "こんにちは";
  std::vector<char16_t> dest;

  std::locale loc = std::locale(""); // 現在のロケールを取得
  std::codecvt_utf16<char> cvt(loc);

  std::mbstate_t state = {};
  const char* p = src.c_str();
  char16_t* q = &dest[0];

  while (true) {
    std::codecvt_result result = cvt.in(p, p + src.size(), state, q, q + dest.size(), false);

    if (result == std::codecvt_result::success) {
      break;
    } else if (result == std::codecvt_result::error) {
      std::cerr << "エンコーディングエラー" << std::endl;
      return 1;
    } else if (result == std::codecvt_result::partial) {
      // 不完全なマルチバイト文字
      // 次回の呼び出しのために state を更新する
    }
  }

  std::cout << dest << std::endl;

  return 0;
}

Boost.Locale ライブラリを使用する

Boost.Locale ライブラリは、std::locale クラスよりも多くの文字エンコーディングとロケール機能をサポートするライブラリです。std::mbrtoc16 関数よりも柔軟性が高く、エンコーディングエラーやサロゲートペアの処理をより簡単に処理できます。

#include <iostream>
#include <boost/locale.hpp>
#include <string>
#include <vector>

int main() {
  std::string src = "こんにちは";
  std::vector<char16_t> dest;

  boost::locale::generator gen(boost::locale::modifiers("")); // 現在のロケールを取得
  boost::locale::locale loc = gen();

  boost::locale::typedefs::utf16_facet utf16_facet;
  boost::locale::streambuf<char16_t> sb(loc, &utf16_facet);

  std::istream_iterator<char> it(src.c_str(), src.c_str() + src.size());
  std::ostream_iterator<char16_t> ot(&sb);

  std::copy(it, it.end(), ot);

  std::cout << dest << std::endl;

  return 0;
}

カスタム関数を使用する

特定のニーズに合わせたカスタム関数を開発することもできます。これは、パフォーマンスが重要である場合や、特定のエンコーディングを扱う必要がある場合に役立ちます。

#include <iostream>
#include <string>

size_t my_mbrtoc16(char16_t* dest, const char* src, size_t n, mbstate_t* state) {
  // カスタムのマルチバイト文字から UTF-16 文字への変換処理
  // ...
}

int main() {
  char16_t dest[2];
  char src[] = "こんにちは";
  mbstate_t state = {};

  size_t n = my_mbrtoc16(dest, src, sizeof(src) - 1, &state);

  if (n == -1) {
    std::cerr << "エンコーディングエラー" << std::