60
60

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Visual C++ 文字列 まとめ

Last updated at Posted at 2018-01-20

第2.0版 (自作)文字列変換関数を追加

#はじめに
C# では文字列型は System.String だけです。一方、Visual C++ では、C 言語との互換性、Win16 との互換性、Win32 との互換性、テンプレート等々の関連で文字列とみなされる型はいろいろあります。

文字列は Visual Studio 2003 までは SJIS が標準だったらしいですが、Visual Studio 2005 からは Unicode (ワイド文字列) が標準になったそうで、混乱に輪がかかっています。よって、古いソースを Visual Studio 2017 でビルドすると、エラーがずらりと表示されます。

ここでは、それらについてまとめてみました。

#どんな「文字列」があるか?
Visual C++ にはどんな「文字列」があるか、ざっくり見てみましょう。もしかしたら、もっとあるかもしれませんが、比較的、目にするのはこんな感じです。

  • char*, wchar_t* (C 言語互換の文字列型)
  • LPSTR などの Windows.h で定義されたマクロ (多数あり)
  • string, wstring (STL の文字列)
  • CString (MFC の文字列)
  • CStringT (MFC のテンプレート文字列)
  • BSTR (COM の文字列)
  • CComBSTR (ATL の文字列)
  • String ^ (CLI の文字列)

#2つの Win32 API 関数
Win32 API 関数には、関数名の最後に A が付くものと W が付くものがあります。前者はマルチバイト文字列 (SJIS) 対応バージョン、後者がワイド文字列 (Unicode) 対応バージョンです。

これらの関数で関数名の最後の A や W のないバージョンがあって、現在は W 付きのバージョンの別名になっています。「現在は」の意味は、過去は A 付きのバージョンの別名になっていたためです。これは、Windows 9x までは OS 内部でも文字列のコードが Unicode でなく、日本語版の場合は、SJIS になっていたためです。

次のサンプルは、コンピュータ名を表示するものですが、最初の方がワイド文字列 (Unicode) バージョン、後の方が マルチバイト文字列 (SJIS) バージョンです。

こちらは GetComputerNameW を使用しています。stdafx.h には #include <Windows.h> を追加しておく必要があります。

#include "stdafx.h"

int main()
{
	WCHAR compName[80];
	DWORD buffSize = sizeof(compName);
	if (GetComputerNameW(compName, &buffSize) != 0)
	{
		wprintf_s(L"%s\n", compName);
	}
	else
	{
		printf_s("エラー\n");
	}

	getchar();
	return 0;
}

こちらは GetComputerNameA を使用しています。stdafx.h には #include <Windows.h> を追加しておく必要があります。

#include "stdafx.h"

int main()
{
	CHAR compName[80];
	DWORD buffSize = sizeof(compName);
	if (GetComputerNameA(compName, &buffSize) != 0)
	{
		printf_s("%s\n", compName);
	}
	else
	{
		printf_s("エラー\n");
	}

	getchar();
	return 0;
}

#tchar.h
tchar.h はプロジェクトを作成すると stdafx.h に自動的に含まれています。これに含まれるマクロを使用することで、同じソースからマルチバイト文字列 (SJIS)、ワイド文字列 (Unicode) のアプリをビルドできます。

このヘッダーファイルには次のようなマクロが含まれています。

##汎用テキストマッピング用のプリプロセッサ ディレクティブ
次のディレクティブはプロジェクトのプロパティで設定します。

#define コンパイル後のアプリ状態
_UNICODE ワイド文字列 (Unicode) 対応
_MBCS マルチバイト文字列 (SJIS) 対応
なし ASCII 文字列対応

##汎用テキストのデータ型のマップ
次のマクロを使うと、_UNICODE, _MBCS の定義に従って(上の表のような)アプリがビルドされます。

マクロ なし _MBCS _UNICODE
_TCHAR char char wchar_t
_TINT int unsigned int wint_t
_TSCHAR signed char signed char wchar_t
_TUCHAR unsigned char unsigned char wchar_t
_TXCHAR char unsigned char wchar_t
_T 影響なし (プリプロセッサによって削除される) 影響なし (プリプロセッサによって削除される) L (後続の文字または文字列を対応する Unicode の文字または文字列に変換する)

※ _T は _TEXT の省略形なので、_TEXT と書くこともできる。

例えば、_UNICODE が定義されている場合、次の2行は同じ結果になります。_UNICODE が定義されていない場合は、最初のほうはエラーとなるが、2行目のほうは正しくコンパイルされます。

	wprintf_s(L"%s\n", str);
	wprintf_s(_T("%s\n"), str);

#Windows.h
Win32 API 関数を使う場合、下のソースのように stdafx.h に Windows.h を手動で追加する必要があります。

// stdafx.h : 標準のシステム インクルード ファイルのインクルード ファイル、または
// 参照回数が多く、かつあまり変更されない、プロジェクト専用のインクルード ファイル
// を記述します。
//
#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

// TODO: プログラムに必要な追加ヘッダーをここで参照してください
#include <Windows.h>

これにより Win32 API 関数を使う際に使用されるマクロ(型名)も使用可能になります。これらの型名は Win16 からのもので、16 / 32/ 64 ビットでコンパイルする場合に互換性があります。

これらの型には PSTR, LPSTR, LPCSTR, LPCTSTR など (その他多数) があります。これは推測ですが、P はポインタ, L は Long、C は定数、T は Text または Typed を意味します。L (Long) は、Win16 時代に関連するもので Win32 では L がなくても同じになります (たぶん)。T が付いた型は、Unicode、SJIS に関わらず正しくコンパイルできる型と思われます。

これらマクロの定義ですが、マルチバイト文字列の場合、次のような感じになるはずです(推測ですが)。ワイド文字列では char が wchar_t に変わります。

  • PSTR char*
  • LPSTR char*
  • LPCSTR const char*
  • LPCTSTR _TEXT(const char*)

因みに、Win16 時代だと PSTR は near char*、LPSTR は far char* とか near, far キーワードを使って表現されていたはずです。

#MFC, ATL ヘッダー
Visual Studio でプロジェクトを作成するとき、MFC や ATL の使用の有無を指定できます。これにより、CString などのクラスが使用できるようになります。

##CString
これは、MFC に含まれる文字列クラスです。MFC に含まれるクラスのメソッドのパラメータなどとしてよく使われます。これは推測ですが、旧型 Visual Basic の文字列をクラス化したもののように見えます。

C の文字列 (char*) を直接、扱うより便利なのですが、スレッドセーフではないようなので注意が必要です。

テンプレート版の CStringT というのもありますが、MFC のメソッドでは使われていないので、わざわざ覚える必要はないと思います。

##BSTR と CComBSTR
COM (Common Object Model) での文字列型は常に BSTR (ワイド文字列) です。ATL ではこれをカプセル化した CComBSTR というクラスでよく扱います。

#マルチバイト文字とワイド文字の変換

マルチバイト文字とワイド文字の変換のために、下のリンク先のようなマクロが用意されています。

 ATL and MFC String Conversion Macros

マクロは ATL3.0 と ATL7.0 のものがあり、どちらも利用可能です。
特徴ですが、ATL3.0 は関数のような使い勝手、ATL7.0 は宣言のような使い勝手です。
ATL3.0 のマクロを使う場合は、USES_CONVERSION マクロを事前に実行しておく必要があります。

USES_CONVERSION;
BSTR bstr = A2W(szMsg);

#(自作)文字列変換関数
Visual C++ には、いろいろな文字列がありますが、たまに相互に変換したいときがあります。
そんなときのために変換関数を作りました。
自作かつ検証も十分でないので、どれだけ実用になるのかも問題ですが。:tongue:

  • Char^ CCharToClrChar(wchar_t c)
  • wchar_t ClrCharToCChar(Char ^c)
  • String^ BSTRToString(BSTR bstr)
  • BSTR ClrStringToBSTR(String ^str)
  • void ClrStringToBSTR(String ^str, BSTR* pbstr)
  • BSTR AToW(LPSTR mbcs)
  • void AToW(LPSTR mbcs, BSTR* wcs)
  • LPSTR WToA(BSTR wcs)
  • void WToA(BSTR wcs, LPSTR* mbcs)
  • String^ CStrToCliStr(CString str)
  • CString CliStrToCStr(String ^str)
  • String^ StdToCliStr(std::wstring str)
  • std::wstring CliStrToStd(String ^str)

##使用方法

後述する StringConvertLib.h をインクルードするだけで使用できます。stdafx.h の内容は特に修正しません。プロジェクトの文字コードは必ずワイド文字にします。

##関数一覧

宣言
Char^ CCharToClrChar(wchar_t c)
機能
ワイド文字を CLR 文字に変換する。
パラメータ
[in] wchar_t c: ワイド文字
戻り値
変換された CLR 文字

宣言
 wchar_t ClrCharToCChar(Char ^c)
機能
 CLR 文字からワイド文字に変換する。
パラメータ
 [in] Char ^c: CLR 文字
戻り値
 変換されたワイド文字

宣言
 String^ BSTRToString(BSTR bstr)
機能
 ワイド文字列 (BSTR) から CLR 文字列に変換する。BSTR は wchar_t* と同じ。
パラメータ
 [in] BSTR bstr: ワイド文字列
戻り値
 変換された CLR 文字列

宣言
 BSTR ClrStringToBSTR(String ^str)
機能
 CLR 文字列からワイド文字列 (BSTR) に変換する。BSTR は wchar_t* と同じ。
パラメータ
 [in] String ^str: CLR 文字列
戻り値
 変換されたワイド文字列 (BSTR)
注意
 戻り値は不要になったら SysAllocString 関数を使って解放すること。

宣言
 void ClrStringToBSTR(String ^str, BSTR pbstr)*
機能
 CLR 文字列からワイド文字列 (BSTR) に変換する。BSTR は wchar_t* と同じ。
パラメータ
 - [in] String ^str: CLR 文字列
 - [out] 変換されたワイド文字列 (BSTR)
戻り値
 なし
注意
 戻り値は不要になったら SysAllocString 関数を使って解放すること。

宣言
 BSTR AToW(LPSTR mbcs)
機能
 マルチバイト文字列からワイド文字列に変換する。
パラメータ
 [in] LPSTR mbcs: マルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列)
戻り値
 変換されたワイド文字列。BSTR は wchar_t* と同じ。
注意
 戻り値は不要になったら delete すること。

宣言
 LPSTR WToA(BSTR wcs)
機能
 ワイド文字列からマルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列) に変換する。
パラメータ
 [in] BSTR wcs: ワイド文字列。BSTR は wchar_t* と同じ。
戻り値
 変換されたマルチバイト文字列
注意
 戻り値は不要になったら delete すること。

宣言
 void AToW(LPSTR mbcs, BSTR wcs)*
機能
 マルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列) からワイド文字列に変換する。
パラメータ
 - [in] LPSTR mbc: マルチバイト文字列
 - [out] BSTR* wcs: 変換されたワイド文字列。BSTR* は wchar_t** と同じ。
戻り値
 なし
注意
 wcs は不要になったら delete すること。

宣言
 void WToA(BSTR wcs, LPSTR mbcs)*
機能
 ワイド文字列からマルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列) に変換する。
パラメータ
 - [in] BSTR wcs: ワイド文字列 (BSTR は wchar_t* と同じ)
 - [out] LPSTR* mbcs: 変換されたマルチバイト文字列 (日本語版 Windows では Shift_JIS 文字列)。LPSTR* は char** と同じ
戻り値
 なし
注意
 mbcs は不要になったら delete すること。

宣言
 String^ CStrToCliStr(CString str)
機能
 CStringT から CLI String に変換する。
パラメータ
 [in] CString str: MFC 互換 CString 文字列
戻り値
 変換された CLR 文字列

宣言
 CString CliStrToCStr(String ^str)
機能
 CLI String から CStringT に変換する。
パラメータ
 [in] String ^str: CLR 文字列
戻り値
 変換された MFC 互換 CString 文字列

宣言
 String^ StdToCliStr(std::wstring str)
機能
 STL のワイド文字列を CLR 文字列に変換する。
パラメータ
 [in] std::wstring str: STL のワイド文字列
戻り値
 変換された CLR 文字列

宣言
 std::wstring CliStrToStd(String ^str)
機能
 CLR 文字列を STL のワイド文字列に変換する。
パラメータ
 [in] String ^str: CLR 文字列
戻り値
 変換された STL のワイド文字列

##使用例

// TestConvertString.cpp : メイン プロジェクト ファイルです。

#include "stdafx.h"
#include "StringConvertLib.h"
using namespace System;
using namespace StringConvertLib;

int main(array<System::String ^> ^args)
{
  Console::WriteLine(L"==== StringConvertLib のテスト ====\n");

  // C スタイル文字から CLR 文字へ
  Console::WriteLine("C スタイル文字から CLR 文字へ");
  Char ^c = CCharToClrChar('C');
  Console::WriteLine(c);

  // CLR 文字からCスタイル文字へ
  char cc = ClrCharToCChar(c);
  printf_s("%c\n", cc);

  // Cスタイル文字列から CLR 文字列へ
  String ^clistr = BSTRToString(L"Cスタイル文字列");
  Console::WriteLine(clistr);

  // CLR 文字列から C スタイル文字列へ
  String ^clistr2 = L"CLR 文字列";
  setlocale(LC_ALL, "ja-JP");
  BSTR bstr = ClrStringToBSTR(clistr2);
  wprintf_s(L"%ls\n", bstr);
  SysFreeString(bstr);

  // CLR 文字列から C スタイル文字列へ (marshaling バージョン)
  clistr2 = L"CLR 文字列 marshal";
  ClrStringToBSTR(clistr2, &bstr);
  wprintf_s(L"%ls\n", bstr);

  // マルチバイト文字列からワイド文字列へ
  BSTR bstr2 = AToW("AToW");
  wprintf_s(L"%ls\n", bstr2);
  delete bstr2;

  // ワイド文字列からマルチバイト文字列へ
  LPSTR pstr = WToA(L"WToA");
  printf_s("%s\n", pstr);
  delete pstr;

  // CStringT から CLI String へ
  CString atlstr = L"CStringT";
  String ^clistr3 = CStrToCliStr(atlstr);
  Console::WriteLine(clistr3);

  // CLI String から CStringT へ
  String ^clistr4 = L"CLI String";
  CString atlstr2 = CliStrToCStr(clistr4);
  wprintf_s(L"%ls\n", atlstr2);

  // std::wstring から CLI String へ
  std::wstring ststr = L"std::wstring";
  String ^clistr5 = StdToCliStr(ststr);
  Console::WriteLine(clistr5);

  // CLI String から std::wstring へ
  String ^clistr6 = L"CLI String";
  std::wstring ststr2 = CliStrToStd(clistr6);
  wprintf_s(L"%ls\n", ststr2.c_str());

  // 終わり
  getchar();
  return 0;
}

#ソース (StringConvetLib.h)

#pragma once
#include <Windows.h>
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
#include <msclr/marshal.h>
#include <atlbase.h>
#include <atlconv.h>
#include <atlstr.h>
#include <string>

using namespace System;
using namespace msclr::interop;

#define BUFFLEN 2048

namespace StringConvertLib {
	//
	//  C スタイル文字から CLR 文字へ
	//  ==============================
	Char^ CCharToClrChar(wchar_t c)
	{
		Char ^gc = gcnew Char(c);
		return gc;
	}

	//
	//  CLR 文字からCスタイル文字へ
	//  ============================
	wchar_t ClrCharToCChar(Char ^c)
	{
		wchar_t wc = (wchar_t)c;
		return wc;
	}

	// Cスタイル文字列から CLR 文字列へ
	//   BSTR は wchar_t* と同じ
	String^ BSTRToString(BSTR bstr)
	{
		String^ str = gcnew String(bstr);
		return str;
	}

	// CLR 文字列から C スタイル文字列へ
	BSTR ClrStringToBSTR(String ^str)
	{
		array<Char>^ buff = str->ToCharArray();
		pin_ptr<Char> wptr = &buff[0];
		BSTR bstr = SysAllocString(wptr);
		return bstr;
	}

	// CLR 文字列から C スタイル文字列へ (marshaling バージョン)
	//   msclr/marshal.h が必要
	void ClrStringToBSTR(String ^str, BSTR* pbstr)
	{
		marshal_context^ context = gcnew marshal_context();
		const wchar_t* str2 = context->marshal_as<const wchar_t*>(str);
		*pbstr = SysAllocString(str2);
	}

	// マルチバイト文字列からワイド文字列へ
	BSTR AToW(LPSTR mbcs)
	{
		size_t rv;
		wchar_t buffer[BUFFLEN];
		mbstowcs_s(&rv, buffer, BUFFLEN, mbcs, _TRUNCATE);
		size_t n = 2 * wcslen((const wchar_t*)buffer) + 2;
		wchar_t* p = new wchar_t[n];
		wcscpy_s(p, n, buffer);
		return p;
	}

	// ワイド文字列からマルチバイト文字列へ
	LPSTR WToA(BSTR wcs)
	{
		size_t rv;
		char buffer[BUFFLEN];
		setlocale(LC_CTYPE, "ja-JP");
		wcstombs_s(&rv, buffer, BUFFLEN, wcs, _TRUNCATE);
		size_t n = strlen(buffer);
		n++;
		char* astr = new char[n];
		strcpy_s(astr, n, buffer);
		return astr;
	}

	// マルチバイト文字列からワイド文字列へ
	//   atlconv.h が必要
	void AToW(LPSTR mbcs, BSTR* wcs)
	{
		ATL::CA2W ws(mbcs);
		size_t n = 2 * wcslen((wchar_t*)ws) + 2;
		*wcs = new wchar_t[n];
		wcscpy_s((wchar_t*)*wcs, n, (wchar_t*)ws);
		return;
	}

	// ワイド文字列からマルチバイト文字列へ
	//   atlconv.h が必要
	void WToA(BSTR wcs, LPSTR* mbcs)
	{
		setlocale(LC_CTYPE, "ja-JP");
		ATL::CW2A str(wcs);
		size_t n = strlen((char*)str) + 1;
		*mbcs = new char[n];
		strcpy_s(*mbcs, n, (char*)str);
		return;
	}

	// CStringT から CLI String へ
	String^ CStrToCliStr(CString str)
	{
		String^ clistr = gcnew String(str.GetBuffer());
		return clistr;
	}

	// CLI String から CStringT へ
	CString CliStrToCStr(String ^str)
	{
		array<Char>^ buff = str->ToCharArray();
		pin_ptr<Char> wptr = &buff[0];
		return CString(wptr);
	}

	// std::wstring から CLI String へ
	String^ StdToCliStr(std::wstring str)
	{
		String ^clistr = gcnew String(str.c_str());
		return clistr;
	}

    // CLI String から std::wstring へ
	std::wstring CliStrToStd(String ^str)
	{
		array<Char>^ buff = str->ToCharArray();
		pin_ptr<Char> wptr = &buff[0];
		return std::wstring((wchar_t*)wptr);
	}
}

60
60
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
60
60

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?