Help us understand the problem. What is going on with this article?

マルチバイト文字列(std::string)とワイド文字列(std::wstring)の間の変換を行うライブラリを作りました(SJIS, UTF-8, UTF-16に対応。SJIS⇔UTF-8の変換も可能)

C++でWindowsアプリを作る場合に、マルチバイト文字列(std::string)とワイド文字列(std::wstring)の間で変換しなければならないことがあります。今回は、Windows API の MultiByteToWideChar() と WideCharToMultiByte() を使って変換を行うライブラリ(ヘッダーオンリー)を作ってみました(文末の strconv.h を保存してお使いください。C++専用です。Cでは使えません)。また、内部的にワイド文字列を経由することで SJIS⇔UTF-8 の変換(マルチバイト文字列同士の変換)を行う関数も用意しています。

  • C++11 以降のコンパイラとC++11 以前のコンパイラの両方で動作するように工夫してあります(但し、ご利用になる場合にはコンパイラのバージョンを気にする必要はありません)。
  • 文末に記したやり方で、日本語EUC や IBM EBCDIC (日本語および日本語カタカナ)等を含むWindows OSがサポートするあらゆるコードページとの相互変換も可能となります。(そのため、ここで紹介したやり方は多くの企業/デベロッパーで利用されています)
  • Visual C++, MinGW の 32bit/64bit 版でコンパイル・動作を確認しています。
  • 添付の strconv.h のライセンスを MIT License or Public Domain のデュアルライセンスにしました。詳しくはこちらをご覧ください。 Public Domain に関しては https://ja.wikipedia.org/wiki/Unlicense もご覧ください。

使用例1(ライブラリをそのまま使用)

main1.cpp
#include <windows.h>
#include "strconv.h"

int main(int argc, char **argv)
{
  std::string utf8_str = u8"あいう";
  printf("utf8_str.length()=%u\n", utf8_str.length());
  std::wstring wide_str = utf8_to_wide(utf8_str);
  printf("wide_str.length()=%u\n", wide_str.length());
  std::string sjis_str = wide_to_sjis(wide_str);
  printf("sjis_str.length()=%u\n", sjis_str.length());
  printf("sjis_str.c_str()=%s\n", sjis_str.c_str());
  return 0;
}

実行結果
>main1.exe
utf8_str.length()=9
wide_str.length()=3
sjis_str.length()=6
sjis_str.c_str()=あいう

使用例2(Cygwin/Msys2環境とコマンドプロンプトの両方での実行に対応)

main2.cpp
#include <windows.h>
#include "strconv.h"

static std::string wide_to_console(const std::wstring &s) {
  if (getenv("ORIGINAL_PATH")) {
    if (getenv("TERM") && !strcmp(getenv("TERM"), "cygwin")) return wide_to_ansi(s); // Cygwin Bash Console
    return wide_to_utf8(s); // Cygwin/Msys2 Terminal
  } else {
    return wide_to_ansi(s);
  }
}

static std::string utf8_to_console(const std::string &s) {
  if (getenv("ORIGINAL_PATH")) {
    if (getenv("TERM") && !strcmp(getenv("TERM"), "cygwin")) return utf8_to_ansi(s); // Cygwin Bash Console
    return s; // Cygwin/Msys2 Terminal
  } else {
    return utf8_to_ansi(s);
  }
}

int main(int argc, char **argv)
{
  std::string utf8_str = u8"あいう";
  printf("utf8_str.length()=%u\n", utf8_str.length());
  std::wstring wide_str = utf8_to_wide(utf8_str);
  std::string cons_str1 = utf8_to_console(utf8_str);
  printf("cons_str1.c_str()=%s\n", cons_str1.c_str());
  std::string cons_str2 = wide_to_console(wide_str);
  printf("cons_str2.c_str()=%s\n", cons_str2.c_str());
  return 0;
}

実行結果(コマンドプロンプトおよびCygwin-Bash-ConsoleではSJIS、Cygwin/Msys2-TerminalではUTF-8で出力されます)
>main2.exe
utf8_str.length()=9
cons_str1.c_str()=あいう
cons_str2.c_str()=あいう

関数一覧

std::wstring ansi_to_wide(const std::string &s)

システムロケールにおける既定のコードページの文字コードからワイド文字列への変換を行います。システムロケールが「日本/日本語」の場合は sjis_to_wide() と同じ結果となります。

std::string wide_to_ansi(const std::wstring &s)

ワイド文字列からシステムロケールにおける既定のコードページの文字コードへの変換を行います。システムロケールが「日本/日本語」の場合は wide_to_sjis() と同じ結果となります。

std::wstring sjis_to_wide(const std::string &s)

シフトJIS(コードページ932)からワイド文字列への変換を行います。

std::string wide_to_sjis(const std::wstring &s)

ワイド文字列からシフトJIS(コードページ932)への変換を行います。

std::wstring utf8_to_wide(const std::string &s)

UTF-8からワイド文字列への変換を行います。

std::string wide_to_utf8(const std::wstring &s)

ワイド文字列からUTF-8への変換を行います。

std::string ansi_to_utf8(const std::string &s)

システムロケールにおける既定のコードページの文字コードからUTF-8への変換を行います。システムロケールが「日本/日本語」の場合は sjis_to_utf8() と同じ結果となります。

std::string utf8_to_ansi(const std::string &s)

UTF-8からシステムロケールにおける既定のコードページの文字コードへの変換を行います。システムロケールが「日本/日本語」の場合は utf8_to_sjis() と同じ結果となります。

std::string sjis_to_utf8(const std::string &s)

シフトJIS(コードページ932)からUTF-8への変換を行います。

std::string utf8_to_sjis(const std::string &s)

UTF-8からシフトJIS(コードページ932)への変換を行います。

添付ファイル(strconv.h)

  • 2019/02/27 23:23 久しぶりに、strconv.h を修正しました。変更点は全関数を inline にすることで、「関数が使われてない」旨の警告が出ないようになりました。確認してませんが、この修正で若干処理速度が上がるかもしれません。機能的には全く変わってませんのでご安心ください。
  • https://github.com/javacommons/strconv の方にもコード(strconv.h)を置いています。この記事内には最新のコードを貼り付けておきます。
strconv.h
/* strconv.h v1.0.0                */
/* Last Modified: 2019/02/27 23:23 */
#ifndef STRCONV_H
#define STRCONV_H

#include <windows.h>
#include <string>
#include <vector>

#if __cplusplus >= 201103L
static inline std::wstring cp_to_wide(const std::string &s, UINT codepage)
{
  int in_length = (int)s.length();
  int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); 
  std::wstring result(out_length, L'\0');
  if (out_length) MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &result[0], out_length);
  return result;
}
static inline std::string wide_to_cp(const std::wstring &s, UINT codepage)
{
  int in_length = (int)s.length();
  int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); 
  std::string result(out_length, '\0');
  if (out_length) WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &result[0], out_length, 0, 0); 
  return result;
}
#else /* __cplusplus < 201103L */
static inline std::wstring cp_to_wide(const std::string &s, UINT codepage)
{
  int in_length = (int)s.length();
  int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); 
  std::vector<wchar_t> buffer(out_length);
  if (out_length) MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &buffer[0], out_length);
  std::wstring result(buffer.begin(), buffer.end());
  return result;
}
static inline std::string wide_to_cp(const std::wstring &s, UINT codepage)
{
  int in_length = (int)s.length();
  int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); 
  std::vector<char> buffer(out_length);
  if (out_length) WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &buffer[0], out_length, 0, 0);
  std::string result(buffer.begin(), buffer.end());
  return result;
}
#endif

static inline std::string cp_to_utf8(const std::string &s, UINT codepage)
{
  if (codepage == CP_UTF8) return s;
  std::wstring wide = cp_to_wide(s, codepage);
  return wide_to_cp(wide, CP_UTF8);
}
static inline std::string utf8_to_cp(const std::string &s, UINT codepage)
{
  if (codepage == CP_UTF8) return s;
  std::wstring wide = cp_to_wide(s, CP_UTF8);
  return wide_to_cp(wide, codepage);
}

static inline std::wstring ansi_to_wide(const std::string &s)
{
  return cp_to_wide(s, CP_ACP);
}
static inline std::string wide_to_ansi(const std::wstring &s)
{
  return wide_to_cp(s, CP_ACP);
}

static inline std::wstring sjis_to_wide(const std::string &s)
{
  return cp_to_wide(s, 932);
}
static inline std::string wide_to_sjis(const std::wstring &s)
{
  return wide_to_cp(s, 932);
}

static inline std::wstring utf8_to_wide(const std::string &s)
{
  return cp_to_wide(s, CP_UTF8);
}
static inline std::string wide_to_utf8(const std::wstring &s)
{
  return wide_to_cp(s, CP_UTF8);
}

static inline std::string ansi_to_utf8(const std::string &s)
{
  return cp_to_utf8(s, CP_ACP);
}
static inline std::string utf8_to_ansi(const std::string &s)
{
  return utf8_to_cp(s, CP_ACP);
}

static inline std::string sjis_to_utf8(const std::string &s)
{
  return cp_to_utf8(s, 932);
}
static inline std::string utf8_to_sjis(const std::string &s)
{
  return utf8_to_cp(s, 932);
}

#endif /* STRCONV_H */

関数一覧にないコードページへの対応方法

以下の関数の UINT codepage 引数に文末の表のコードページを渡すことで様々な文字コード(日本語EUC・JIS等も含む)に対応することができます。関数一覧に挙げた関数の実際の定義を参考にして、以下の関数をラップする関数を作るのが良いかもしれません。便利なら、strconv.h の末尾にそれらの関数を付け加えても構いません。UINT codepage に 932 を渡している、sjis_to_~() や ~_to_sjis() 関数が参考になるでしょう。932 は文末のコードページ一覧表に「日本語 (シフト JIS)」として載っています。

  • std::wstring cp_to_wide(const std::string &s, UINT codepage)
  • std::string wide_to_cp(const std::wstring &s, UINT codepage)
  • std::string cp_to_utf8(const std::string &s, UINT codepage)
  • std::string utf8_to_cp(const std::string &s, UINT codepage)

(コードページ一覧表。長いので折りたたみ中。展開してご覧ください)
コードページ 説明
37 IBM EBCDIC (米国 - カナダ)
437 OEM 米国
500 IBM EBCDIC (インターナショナル)
708 アラビア語 (ASMO 708)
720 アラビア語 (DOS)
737 ギリシャ語 (DOS)
775 バルト言語 (DOS)
850 西ヨーロッパ言語 (DOS)
852 中央ヨーロッパ言語 (DOS)
855 OEM キリル
857 トルコ語 (DOS)
858 OEM マルチリンガル ラテン I
860 ポルトガル語 (DOS)
861 アイスランド語 (DOS)
862 ヘブライ語 (DOS)
863 フランス語 (カナダ) (DOS)
864 アラビア語 (864)
865 北欧 (DOS)
866 キリル言語 (DOS)
869 ギリシャ語, Modern (DOS)
870 IBM EBCDIC (多国語ラテン 2)
874 タイ語 (Windows)
875 IBM EBCDIC (ギリシャ語 Modern)
932 日本語 (シフト JIS)
936 簡体字中国語 (GB2312)
949 韓国語
950 繁体字中国語 (Big5)
1026 IBM EBCDIC (トルコ語ラテン 5)
1047 IBM ラテン-1
1140 IBM EBCDIC (米国 - カナダ - ヨーロッパ)
1141 IBM EBCDIC (ドイツ - ヨーロッパ)
1142 IBM EBCDIC (デンマーク - ノルウェー - ヨーロッパ)
1143 IBM EBCDIC (フィンランド - スウェーデン - ヨーロッパ)
1144 IBM EBCDIC (イタリア - ヨーロッパ)
1145 IBM EBCDIC (スペイン - ヨーロッパ)
1146 IBM EBCDIC (英国 - ヨーロッパ)
1147 IBM EBCDIC (フランス - ヨーロッパ)
1148 IBM EBCDIC (インターナショナル - ヨーロッパ)
1149 IBM EBCDIC (アイスランド語 - ヨーロッパ)
1200 Unicode
1201 Unicode (ビッグ エンディアン)
1250 中央ヨーロッパ言語 (Windows)
1251 キリル言語 (Windows)
1252 西ヨーロッパ言語 (Windows)
1253 ギリシャ語 (Windows)
1254 トルコ語 (Windows)
1255 ヘブライ語 (Windows)
1256 アラビア語 (Windows)
1257 バルト言語 (Windows)
1258 ベトナム語 (Windows)
1361 韓国語 (Johab)
10000 西ヨーロッパ言語 (Mac)
10001 日本語 (Mac)
10002 繁体字中国語 (Mac)
10003 韓国語 (Mac)
10004 アラビア語 (Mac)
10005 ヘブライ語 (Mac)
10006 ギリシャ語 (Mac)
10007 キリル言語 (Mac)
10008 簡体字中国語 (Mac)
10010 ルーマニア語 (Mac)
10017 ウクライナ語 (Mac)
10021 タイ語 (Mac)
10029 中央ヨーロッパ言語 (Mac)
10079 アイスランド語 (Mac)
10081 トルコ語 (Mac)
10082 クロアチア語 (Mac)
20000 繁体字中国語 (CNS)
20001 TCA 台湾
20002 繁体字中国語 (Eten)
20003 IBM5550 台湾
20004 TeleText 台湾
20005 Wang 台湾
20105 西ヨーロッパ言語 (IA5)
20106 ドイツ語 (IA5)
20107 スウェーデン語 (IA5)
20108 ノルウェー語 (IA5)
20127 US-ASCII
20261 T.61
20269 ISO-6937
20273 IBM EBCDIC (ドイツ)
20277 IBM EBCDIC (デンマーク - ノルウェー)
20278 IBM EBCDIC (フィンランド - スウェーデン)
20280 IBM EBCDIC (イタリア)
20284 IBM EBCDIC (スペイン)
20285 IBM EBCDIC (英国)
20290 IBM EBCDIC (日本語カタカナ)
20297 IBM EBCDIC (フランス)
20420 IBM EBCDIC (アラビア語)
20423 IBM EBCDIC (ギリシャ語)
20424 IBM EBCDIC (ヘブライ語)
20833 IBM EBCDIC (韓国語拡張)
20838 IBM EBCDIC (タイ語)
20866 キリル言語 (KOI8-R)
20871 IBM EBCDIC (アイスランド語)
20880 IBM EBCDIC (キリル言語 - ロシア語)
20905 IBM EBCDIC (トルコ語)
20924 IBM ラテン-1
20932 日本語 (JIS 0208-1990 および 0212-1990)
20936 簡体字中国語 (GB2312-80)
20949 韓国語 Wansung
21025 IBM EBCDIC (キリル言語 セルビア - ブルガリア)
21027 拡張アルファベットの小文字
21866 キリル言語 (KOI8-U)
28591 西ヨーロッパ言語 (ISO)
28592 中央ヨーロッパ言語 (ISO)
28593 ラテン 3 (ISO)
28594 バルト言語 (ISO)
28595 キリル言語 (ISO)
28596 アラビア語 (ISO)
28597 ギリシャ語 (ISO)
28598 ヘブライ語 (ISO-Visual)
28599 トルコ語 (ISO)
28603 リトアニア語 (ISO)
28605 ラテン 9 (ISO)
29001 ヨーロッパ
38598 ヘブライ語 (ISO-Logical)
50000 ユーザー定義
50001 自動選択
50220 日本語 (JIS)
50221 日本語 (JIS 1 バイト カタカナ可)
50222 日本語 (JIS 1 バイト カタカナ可 - SO/SI)
50225 韓国語 (ISO)
50227 簡体字中国語 (ISO-2022)
50229 繁体字中国語 (ISO-2022)
50930 IBM EBCDIC (日本語および日本語カタカナ)
50931 IBM EBCDIC (日本語および米国 - カナダ)
50932 日本語 (自動選択)
50933 IBM EBCDIC (韓国語および韓国語拡張)
50935 IBM EBCDIC (簡体字中国語)
50936 簡体字中国語 (自動選択)
50937 IBM EBCDIC (繁体字中国語)
50939 IBM EBCDIC (日本語および日本語 - ラテン語)
50949 韓国語 (自動選択)
50950 繁体字中国語 (自動選択)
51251 キリル言語 (自動選択)
51253 ギリシャ語 (自動選択)
51256 アラビア語 (自動選択)
51932 日本語 (EUC)
51936 簡体字中国語 (EUC)
51949 韓国語 (EUC)
52936 簡体字中国語 (HZ)
54936 簡体字中国語 (GB18030)
57002 ISCII デバナガリ文字
57003 ISCII ベンガル語
57004 ISCII タミール語
57005 ISCII テルグ語
57006 ISCII アッサム語
57007 ISCII オリヤー語
57008 ISCII カンナダ語
57009 ISCII マラヤーラム語
57010 ISCII グジャラート語
57011 ISCII パンジャブ語
65000 Unicode (UTF-7)
65001 Unicode (UTF-8)

このソースコードのライセンス (MIT License or Public Domain)

Public Domain (Unlicense) に関しては https://ja.wikipedia.org/wiki/Unlicense もご覧ください。

Unlicenseは、パブリックドメインに非常に近いライセンスであり、著作権法への抵抗に重点を置いている。2010年1月1日(パブリックドメインの日)に初めて提唱された。Unlicenseはパブリックドメインとして権利を放棄する手段を提供しており、帰属の必要がない非常に緩いライセンスともいえる。2015年にGitHubは、github.com上のライセンスされた全プロジェクト500万弱のうち、2パーセントに当たる約10万のプロジェクトがUnlicenseを採用していると発表した。

------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2019 JavaCommons
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-----------------------------------------------------------------------------
javacommons
TOEICスコアは910点です。絵文字はいかがでしょうか?Qiitaの記事タイトルにも使えますよw 🎧😀😬😁😂😃😄😅😆😇😉😊🙂😋😌😍😘😗😙😚😜😝😛😎😏😶😐😑😒😳😞😟😠😡😔😕🙁😣😖😫😩😤😮😱😨😰😯😦😧😢😥😪😓😭😵😲😷😴💤💩😈👿👹👺💀👻
https://code.dlang.org/packages/pegged-cutter
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした