文字列比較高速化とLinux対応

文字列比較を高速にしました、ついでにLinux(gcc4)対応しました。
http://code.google.com/p/tinysegmenter-cpp/

最初、こんなマクロを作って実行前に展開されて定数になってくれたらいいなと思っていたんですけど、

#define STRING_TO_ULONG(p) \
	( \
		(*(((unsigned char*)p) + 1) == 0) ? *((unsigned char*)p) : \
		( \
			(*(((unsigned char*)p) + 2) == 0) ? ((*(((unsigned char*)p) + 0)) << 8) + ((*(((unsigned char*)p) + 1)) ) : \
			( \
				(*(((unsigned char*)p) + 3) == 0) ? ((*(((unsigned char*)p) + 0)) << 16) + ((*(((unsigned char*)p) + 1)) << 8) + ((*(((unsigned char*)p) + 2)) ) : \
				( \
					((*(((unsigned char*)p) + 0)) << 24) + ((*(((unsigned char*)p) + 1)) << 16) + \
						((*(((unsigned char*)p) + 2)) << 8) + ((*(((unsigned char*)p) + 3)) ) \
				) \
			) \
		) \
	)

これではうまくいかないことが分かった。
マクロ展開しても、 ? で分岐があると事前展開されない、、、まー当たり前か。分岐してみないとどっちに行くかわからないしね。
コンパイラの最適化とかで多少は早くなるんだろうけど、見た目もそうとうアレなので、没にした。

そして、いろいろ悩んだ末に、unsigned long r = 'あ' ってやると、0x82A0 と帰ってくることがわかった。
なんだ、何もしなくていいぢゃないか。

twitter でつぶやいたら、コンパイラによっては unsigned long r = 'あ' が 0x82A0 ではなく、0xA082 と逆になって帰ってくるのもあるよって指摘を受けた。

うーん。
とりあえず、VC++gcc で動けばいいかなと思っている。

VC++gcc でやったところ unsigned long r = 'あ' は 0x82A0 と帰ってきたから、とりあえずコレでいいかなと思ってる。


それと、gcc は unsigned long r = 'あ' のところに warning: multi-character character constant 警告が出る。
これは、 -Wno-multichar オプションで抑制可能。

とりあえず、このオプションをつけてコンパイルする。
VC++ の pragma みたいな奴でエラーが抑制できればいいんだけど、 gcc ではできないようなので、 -Wno-multichar をつけて使ってね。

あと、はまったのが (unsigned long)'ア' が 0FFFFFFB1h とマイナスとして解釈されたところ。
twitter でつぶやいたところ、(unsigned long)(unsigned char)'ア' にすればOKといわれた。

(unsigned long)(unsigned char)'ア' にしたところ 0B1h と正しく変換できた。
なんというか、twitter すげー。。。

んなわけで、正規表現に相当するルーチンはこんな感じになった。

std::string TinySegmenter::ctype_(const std::string & str) const
{
	unsigned long c = stringToULong(str.c_str());	//utf8があるとイヤなんで unsigned longで.

//	this->Pattern[Pattern_M].CreatePattern("[一二三四五六七八九十百千万億兆]");
	unsigned long Pattern_MArray[] = { '一','二','三','四','五','六','七','八','九'
					,'十','百','千','万','億','兆' , 0};
	for (unsigned long * Pattern_MArrayP = Pattern_MArray ; *Pattern_MArrayP ; Pattern_MArrayP++ )
	{
		if (c == *Pattern_MArrayP)
		{
			return "M";
		}
	}

	//	this->Pattern[Pattern_H].CreatePattern("[一-龠々〆ヵヶ]");
	if (c >= (unsigned long)'一' && c <= (unsigned long)'龠')
	{
		return "H";
	}
	unsigned long Pattern_HArray[] = { '々','〆','ヵ','ヶ', 0 };
	for (unsigned long * Pattern_HArrayP = Pattern_HArray ; *Pattern_HArrayP ; Pattern_HArrayP++ )
	{
		if (c == *Pattern_HArrayP)
		{
			return "H";
		}
	}

	//	this->Pattern[Pattern_I].CreatePattern("[ぁ-ん]");
	if (c >= (unsigned long)'ぁ' && c <= (unsigned long)'ん')
	{
		return "I";
	}

	//	this->Pattern[Pattern_K].CreatePattern("[ァ-ヴーア-ン゙ー]");
	if ((c >= ((unsigned long)'ァ') && c <= ((unsigned long)'ヴ') ) || 
		(c >= ((unsigned long)(unsigned char)'ア')  && c <= ((unsigned long)(unsigned char)'ン') ) || 
		(c == ((unsigned long)'ー') || c == ((unsigned long)(unsigned char)'゙') || c == (unsigned long)(unsigned char)'ー' ) )
	{
		return "K";
	}

	//	this->Pattern[Pattern_A].CreatePattern("[a-zA-Za-zA-Z]");
	if ((c >= 'a' && c <= 'z') || 
		(c >= 'A' && c <= 'Z') || 
		(c >= (unsigned long)'a' && c <= (unsigned long)'z') || 
		(c >= (unsigned long)'A' && c <= (unsigned long)'Z')  )
	{
		return "A";
	}

//	this->Pattern[Pattern_N].CreatePattern("[0-90-9]");
	if ((c >= '0' && c <= '9') || 
		(c >= (unsigned long)'0' && c <= (unsigned long)'9')  )
	{
		return "N";
	}

	return "O";
}