.NET Framework 文字列操作

Last-modified: 2006-10-26 (木) 23:17:26

マネージドからアンマネージド文字列へ変換

マネージド文字列(System.String)からアンマネージド文字列(unsigned char*)へ変換するのは面倒なものです。ここでは、System.Stringで与えられた文字列を単にC言語の文字列に変換する手法を2つ紹介します。
(「マネージからアンマネージ文字列」と書いているにも関わらず、最後の手法は結局マネージ文字列です。ま、目的は達成できているから、良しとしよう。)

Web上でよく紹介されている方法 ~1~

これ→http://msdn2.microsoft.com/ja-JP/library/d1ae6tz5.aspx
Win32を使うから論外。

Web上でよく紹介されている方法 ~2~

using namespace System::Runtime::InteropServices;
void * unmanagedChars = (Marshal::StringToHGlobalAnsi(managedString)).ToPointer();
printf("%s\n", unmanagedChars);
// 解放
Marshal::FreeHGlobal(unmanagedChars);

取得した文字列のためのメモリ領域を解放しないとメモリリークします(たぶん)。
「void *」にしていますが、「char *」にキャストすると良いかと思います。
今回は、printfでの表示でしたのでキャストする手間を考え「void *」としました。

[補足]
マネージだからメモリリークしないと思っている人。
確かに、プログラムが終了するときにはリークしないでしょう。
けれど例えば、ある操作を繰り返すたびにメモリが解放されずに残ってしまう
ということは有り得ます。Javaでも同様です。
たとえば、ArrayListにStringを追加し続けているようなものです。

あまり紹介されていない方法

メモリリークを絶対的に阻止したい→できるだけマネージドから離れたくない人
あるいは、メモリ管理を放棄したい人向け。

unsigned char managedChars __gc[] = System::Text::Encoding::Convert(
	System::Text::Encoding::Unicode,
	System::Text::Encoding::GetEncoding("Shift_JIS"),  // 環境依存
	System::Text::Encoding::Unicode->GetBytes(managedString)
);
printf("%s\n", managedChars);  // 標準関数ならそのまま使える?!
char str[65536];  // サイズは、目的に合わせてください。
sprintf(str, "%s", managedChars);
function(str);  // 自前の関数は、sprintfで変換!

環境に合わせて、エンコーディングを変えないとダメだけど、
メモリリークはシステム任せにできる。
エンコーディングの指定は、System::Text::Encoding::GetEncoding(0)でいいのかも。
0を与えると既定のエンコーディングが使われるそうだから。
(→http://msdn2.microsoft.com/ja-jp/library/wzsz3bk3.aspx

補足

あまり文字列操作には関係のない話題ですが。。

シャローコピーとディープコピー

以下のようにしたとき、aとbは同じ文字列ポインタを指しているハズ。

System::String *a, *b;
a = S"Hogehoge";
b = a;

ここで、b=aはシャローコピーになっている。
つまり、aの内容を書き換えるとbの内容も変わるハズ。

なんだけど、実際にはaの内容を書き換える方法がない。
ので、上記のコピーは、一応安全・・・かな?
もっと明示的にディープコピーしたいなら。

b = String::Copy(a);

とする。

検証したコードは、次のような感じ。

System::Diagnostics::Debug::WriteLine(S"------------------------------------ test 1");
{
	System::String *a, *b;
	a = S"Hogehoge";
	b = a;
	Console::WriteLine( String::Format(S"String1 : {0}", a) );
	Console::WriteLine( String::Format(S"string2 : {0}", b) );
	Collections::IEnumerator * enm = a->GetEnumerator();
	while (enm->MoveNext() ) {
		Char * ch = __try_cast<Char*>(enm->Current);
		*ch = 'a';
	}
	Console::WriteLine( String::Format(S"String1 : {0}", a) );
	Console::WriteLine( String::Format(S"string2 : {0}", b) );
}
System::Diagnostics::Debug::WriteLine(S"------------------------------------ test 2");
{
	System::String *a, *b;
	char * str;
	a = S"Hogehoge";
	b = a;
	Console::WriteLine( String::Format(S"String1 : {0}", a) );
	Console::WriteLine( String::Format(S"string2 : {0}", b) );
	using namespace System::Runtime::InteropServices;
	str = (char*)(Marshal::StringToHGlobalAnsi(a)).ToPointer();
	str[0] = 'p';
	Console::WriteLine( String::Format(S"String1 : {0}", a) );
	Console::WriteLine( String::Format(S"string2 : {0}", b) );
}
System::Diagnostics::Debug::WriteLine(S"------------------------------------ test 3");
{
	System::String *a, *b;
	a = S"Hogehoge";
	b = a;
	Console::WriteLine( String::Format(S"String1 : {0}", a) );
	Console::WriteLine( String::Format(S"string2 : {0}", b) );
	System::Text::StringBuilder * sb = new System::Text::StringBuilder(a);
	sb->Replace('H', 'h');
	Console::WriteLine( String::Format(S"String1 : {0}", a) );
	Console::WriteLine( String::Format(S"string2 : {0}", b) );
}
System::Diagnostics::Debug::WriteLine(S"------------------------------------ test 4");
{
	System::String *a, *b;
	a = S"Hogehoge";
	b = String::Copy(a);
	Console::WriteLine( String::Format(S"String1 : {0}", a) );
	Console::WriteLine( String::Format(S"string2 : {0}", b) );
}

次のコードは、aからbに文字列をコピーしたつもりになるコード。

System::Void CopyString(System::String * to, System::String * from)
{
	to = from;
}
int _tmain()
{
	System::String *a, *b;
	a = S"Hogehoge";
	CopyString(b, a);
	Console::WriteLine( String::Format(S"String1 : {0}", a) );
	Console::WriteLine( String::Format(S"string2 : {0}", b) );
	return 0;
}

上記のCopyString()中にある「to = from」をしたところで、
CopyString()中のtoが"Hogehoge"になるだけ。
呼び出し元には何ら影響がない。
どうしてもコピーしたいなら、参照かポインタのポインタを使う。

以上は、もちろん文字列Stringに関することだけではないことを最後に書き加えておく。

[参考]