<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>はらぺこ日誌 &#187; 文字列処理</title>
	<atom:link href="https://blog.harapeko.jp/tag/%e6%96%87%e5%ad%97%e5%88%97%e5%87%a6%e7%90%86/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.harapeko.jp</link>
	<description>株式会社はらぺこ 公式ブログ</description>
	<lastBuildDate>Mon, 30 Oct 2017 14:32:56 +0000</lastBuildDate>
	<language>ja</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.2</generator>
	<item>
		<title>C++11 で Unicode プログラミングのススメ</title>
		<link>https://blog.harapeko.jp/2011/12/27/cpp11-unicode/</link>
		<comments>https://blog.harapeko.jp/2011/12/27/cpp11-unicode/#comments</comments>
		<pubDate>Mon, 26 Dec 2011 16:33:41 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[Boost]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[C++0x]]></category>
		<category><![CDATA[C++11]]></category>
		<category><![CDATA[Unicode]]></category>
		<category><![CDATA[wchar_t]]></category>
		<category><![CDATA[文字セット]]></category>
		<category><![CDATA[文字列処理]]></category>
		<category><![CDATA[正規表現]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=229</guid>
		<description><![CDATA[このエントリは、C++11 Advent Calendar 2011 への参加記事です。 初心者表明を免罪符に [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>このエントリは、<a href="http://atnd.org/events/21936">C++11 Advent Calendar 2011</a> への参加記事です。</p>
<p>初心者表明を免罪符にするつもりは毛頭無いのですが、 C++0x/11 の学習、およびそれを用いた経験はまだまだ浅いため、内容的に拙い部分が多々あることを、あらかじめご容赦願いたいと思います <tt>m(_ _)m</tt> 。ていうか突っ込みだいかんげいでつ。</p>
<p>一応 <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf">ISO/IEC 14882:2011 の draft &#8220;n3242&#8243;</a> を参照しています。 GCC は 4.7 入れるの面倒だったので、動作確認できるものについては Ubuntu 11.10 に入っていた 4.6.1 を用いています。</p>
<h3>Unicode に対応したリテラル</h3>
<p>文字リテラルについてはドラフトの 2.14.3、文字列リテラルについては 2.14.5 に記述があります。<br />
<span id="more-229"></span><br />
文字リテラルには従来の</p>
<pre>
'a'
L'あ'
</pre>
<p>といったスタイルに加えて、</p>
<pre>
u'\u00a9'   // コピーライト記号
U'\U0002000b'  // 丈の右上に点がついた字
</pre>
<p>といったスタイルが追加されました。想定されるべき対応関係を表にすると以下の通りになります。</p>
<table>
<tr>
<th>記述スタイル</th>
<th>文字セット</th>
<th>物理型</th>
</tr>
<tr>
<td><code>'</code>&#8230;<code>'</code></td>
<td>所謂 C 文字。<br />マルチバイトの 1 オクテットでもいいし、まぁ、何でもあり。</td>
<td><code>char</code></td>
</tr>
<tr>
<td><code>l'</code>&#8230;<code>'</code> または <code>L'</code>&#8230;<code>'</code></td>
<td>ユニバーサル文字セット (UCS)。</td>
<td><code>wchar_t</code></td>
</tr>
<tr>
<td><code>u'</code>&#8230;<code>'</code></td>
<td>UTF-16</td>
<td><code>char16_t</code></td>
</tr>
<tr>
<td><code>U'</code>&#8230;<code>'</code></td>
<td>UTF-32</td>
<td><code>char32_t</code></td>
</tr>
</table>
<p>文字列リテラルではさらに <code>u8</code> という接頭子も使えます。</p>
<pre>
u8"Copyright \u00a9 2011 Harapeko, Inc."    // \u00a9 は UTF-8 のオクテット列 [C2 A9] に変換される
u"\U0002000bは「丈」の字にクリソツ"         // \U0002000b は UTF-16 の該当するサロゲートペアに変換される…ハズ
</pre>
<p>対応関係の表は、…面倒くさいからもういいか。</p>
<p>あとさらっと流しちゃいましたが、 Unicode 用のエスケープ文字も追加されました。<q><code>\uNNNN</code></q> は 16bits の、 <q><code>\UNNNNNNNN</code></q> は 32bits の UCS を表現できます。上記の例のように、適切な文字列リテラル内で使用すれば、対応する文字セットの数値列に適宜変換されるはずです。この辺の説明はドラフトの 2.3 にありますが、以下の説明の通り、あくまで UCS の文字値を表現するものであって UTF の数値列を表現するものではないので、 <code>\uNNNN</code> の形式でサロゲートペアの上位代用符号位置に相当する値を指定することはできません。</p>
<blockquote cite="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf" title="Working Draft, Standard for Programming<br />
Language C++ (N3242=11-0012)"><p>
The character designated by the universal-character-name \UNNNNNNNN is that character whose character<br />
short name in ISO/IEC 10646 is NNNNNNNN; the character designated by the universal-character-name \uNNNN<br />
is that character whose character short name in ISO/IEC 10646 is 0000NNNN. If the hexadecimal value for a<br />
universal-character-name corresponds to a surrogate code point (in the range 0xD800.0xDFFF, inclusive),<br />
the program is ill-formed. Additionally, if the hexadecimal value for a universal-character-name outside the<br />
c-char-sequence, s-char-sequence, or r-char-sequence of a character or string literal corresponds to a control<br />
character (in either of the ranges 0&#215;00.0x1F or 0x7F.0x9F, both inclusive) or to a character in the basic<br />
source character set, the program is ill-formed.15
</p></blockquote>
<h3>Unicode に対応した物理型</h3>
<p>Unicode に対応した型については、ドラフトの 3.9.1 に説明があります。重要なのは多分以下の箇所。</p>
<blockquote cite="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf" title="Working Draft, Standard for Programming<br />
Language C++ (N3242=11-0012)">
<ol start="5">
<li>Type wchar_t is a distinct type whose values can represent distinct codes for all members of the largest<br />
extended character set specified among the supported locales (22.3.1). Type wchar_t shall have the same<br />
size, signedness, and alignment requirements (3.11) as one of the other integral types, called its underlying<br />
type. Types char16_t and char32_t denote distinct types with the same size, signedness, and alignment as<br />
uint_least16_t and uint_least32_t, respectively, in &lt;stdint.h&gt;, called the underlying types.</li>
</ol>
</blockquote>
<p>エーゴは苦手なんですが、ここを読む限り、<code>wchar_t</code> はサポートするロケールに含まれるもっとも大きな値の文字値を表現できるのに十分なサイズの整数型であることが補償されてなきゃいけなさそうに見えます。 <code>wchar_t</code> については<a href="http://blog.harapeko.jp/2009/07/25/wchar_t-suck/" title="はらぺこ日誌? ブログアーカイブ ? 頼りなさげな wchar_t">大分昔に見捨てている</a>んですが <tt>(^_^;</tt> 、VC++2010 だと 32bits 整数に変更されていたりするんでしょうか…?</p>
<p># <a href="http://msdn.microsoft.com/ja-jp/library/s3f49ktz.aspx" title="Data Type Ranges (MSDN)">この辺</a>とか見る限り、やっぱり <code>unsigned short int</code> 相当、のままみたいですね… orz</p>
<p><code>char16_t</code> と <code>char32_t</code> は、それぞれ UTF-16、 UTF-32 を扱うための型と考えて差し支えなさそうです。</p>
<h3>「内部文字」のポリシー</h3>
<p>型についての想定を考えるならば、プログラムが内部で扱う文字データは、 C++11 では <code>wchar_t</code> を使用するべきであるように思われます。将来的にはそうなってゆくべきなのでしょう。しかし過去との互換性などの観点から、各ベンダーの <code>wchar_t</code> に対する取り扱いは当面現状維持か、もしくは段階的な仕様変更 (コンパイラオプションでの切り替え等) となっていくことが予想されます。</p>
<p>それに対し、 UTF-32 に関して言えば、恐らく向こう十何年かぐらいは「1要素 = UCS 1文字」であり続けるのではないかと思われます。従って、内部文字への要件として「1文字を 1つの数値のみで扱いたい」というのがあるのであれば、当面は <code>char32_t</code> と <code>U"</code>&#8230;<code>"</code> 形式のリテラルを用いるのが良さそうです。</p>
<table>
<tr>
<th>要件</th>
<th>選択すべき型と文字セット</th>
</tr>
<tr>
<td>
<ul>
<li>1文字を 1つの数値のみで扱いたい</li>
<li>メモリー使用量は気にしないか、32bits 幅でも十分管理可能</li>
</ul>
</td>
<td><code>char32_t</code>、 UTF-32</td>
</tr>
<tr>
<td>
<ul>
<li><code>&lt;</code>(<code>boost/</code>)<code>regex&gt;</code> を使いたい (後述)</li>
<li>UTF-8 のクセに精通しているのでマルチバイトでも気にならない</li>
<li>メモリー使用量を極力抑えたい</li>
</ul>
</td>
<td><code>char</code>、 UTF-8</td>
</tr>
<tr>
<td>
<ul>
<li>とにかく <code>wchar_t</code> を使い慣れている</li>
<li>数十年後の未来との互換性、汎用性に賭けたい</li>
</ul>
</td>
<td><code>wchar_t</code>、 UCS</td>
</tr>
</table>
<h3>char32_t で文字列置換を試してみる</h3>
<p>そんなわけで、実際に UTF-32 を内部文字の文字セットとして採用したプログラム例を作ってみることにしました。内容的には、静的に用意した文字列内のすべての「くま」を「ぱんだ」に置き換える、という簡単なものです。</p>
<pre>
#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;string&gt;

using namespace std;

int main()
{
    u32string before = U&quot;てくまくまやこんてくまくまやこん むらやましゃちょうよ おおきなくまにな～ぁれ&quot;;
    u32string after;
    u32string kuma = U&quot;くま&quot;;
    u32string panda = U&quot;ぱんだ&quot;;

    auto start_it = before.begin();
    auto find_it = start_it + (kuma.size() - 1);
    while (find_it &lt; before.end()) {
        int cnt = 0;
        auto stop_it = find_if(kuma.rbegin(), kuma.rend(), [&amp;cnt, find_it](char32_t letter) {
            return *(find_it - cnt++) != letter;
        });
        if (stop_it != kuma.rend()) {
            find_it += cnt;
            continue;
        }
        // くまを発見、ぱんだに変身!!
        after.append(start_it, find_it - (kuma.size() - 1));
        after += panda;
        start_it = find_it + 1;
        find_it = start_it + (kuma.size() - 1);
    }
    after.append(start_it, find_it);

    cout &lt;&lt; &quot;before: &quot; &lt;&lt; before.size() &lt;&lt; endl;
    cout &lt;&lt; &quot;after: &quot; &lt;&lt; after.size() &lt;&lt; endl;

    return 0;
}
</pre>
<p>えっと… アルゴリズムの説明とかはいいですよね? 文字列の先頭からと検索語の後からで評価して、完全一致しなかった場合は一致した数値の数だけ読み飛ばして、を繰り返すというオーソドックスなやり方です。これだったらかっこつけて <code>find_if</code> とか使わんで <code>for</code> で回しても大して変わらんやんとかそういう突っ込みはさておき <tt>(^_^;</tt> 。</p>
<p>GCC4.6/Ubuntu での実行結果は以下の通り。</p>
<pre>
murachi@ubuntu-vbox:~/otoco/trunk/sample$ g++ -std=c++0x -o kumapan-n kumapan-n.cpp 
murachi@ubuntu-vbox:~/otoco/trunk/sample$ ./kumapan-n
before: 39
after: 44
murachi@ubuntu-vbox:~/otoco/trunk/sample$ 
</pre>
<p>実行結果として置換前後の <code>u32string::size()</code> を表示しています。 5つある「くま」が「ぱんだ」に置き換えられたので、その数が 5 増えています。増える筈の文字数と一致するので、正しく動作しているように見えます。</p>
<h3>iconv を使って実際に出力してみる</h3>
<p>さて、実際の文字列を出力してみたいのですが、このままだとロケールが UTF-8 で動作している端末上では表示できません。ファイルに出力してテキストエディタで、という手もありますが、せっかくなので libiconv を使って指定した文字セットに変換して出力、ということをやってみることにしましょう。</p>
<p>libiconv の利用に際しては、お手製のラッパークラスを作成することで対応しました。作成したソースコードを以下にリンクします。</p>
<ul>
<li><a href="http://developer.harapeko.jp/trac/original/otoco/browser/trunk/sample/EncodeString.hpp?rev=16">EncodeString.hpp</a></li>
<li><a href="http://developer.harapeko.jp/trac/original/otoco/browser/trunk/sample/EncodeString.cpp?rev=16">EncodeString.cpp</a></li>
</ul>
<p>このクラスは<a href="http://blog.harapeko.jp/2010/09/22/utf-8-is-nice/" title="はらぺこ日誌? ブログアーカイブ ? UTF-8 もイマイチだが…">過去の記事</a>においても使用しておりますが、 C++11 の勉強も兼ねて (?)、内部文字に使用する物理型と文字セットをテンプレートパラメータに指定できるテンプレートクラスに書き換えています (あ、過去の記事でのソースへのリンク先が最新版になっちゃってる…直さなきゃ…)。</p>
<p>そして先ほどのサンプルプログラムは、最初の方で <code>EncodeString.hpp</code> を <code>#include</code> し、</p>
<pre>
#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;string&gt;

#include &quot;EncodeString.hpp&quot; // ←

using namespace std;
</pre>
<p>最後の方で出力内容を以下のように修正します。</p>
<pre>
    cout &lt;&lt; &quot;before: &quot; &lt;&lt; EncodeString&lt;char32_t, chset_Utf32&gt;(before, chset_Utf8).getCharArray() &lt;&lt; endl;
    cout &lt;&lt; &quot;after: &quot; &lt;&lt; EncodeString&lt;char32_t, chset_Utf32&gt;(after, chset_Utf8).getCharArray() &lt;&lt; endl;
</pre>
<p>Windows 環境とかで Shift JIS (CP-932) で出力したい人は、 <code>chset_Utf8</code> を <code>chset_Cp932</code> に置き換えてあげれば ok です。GCC4.6/Ubuntu での実行結果は以下の通り。</p>
<pre>
murachi@ubuntu-vbox:~/otoco/trunk/sample$ g++ -std=c++0x -o kumapan EncodeString.cpp kumapan.cpp 
murachi@ubuntu-vbox:~/otoco/trunk/sample$ ./kumapan
before: てくまくまやこんてくまくまやこん むらやましゃちょうよ おおきなくまにな～ぁれ
after: てぱんだぱんだやこんてぱんだぱんだやこん むらやましゃちょうよ おおきなぱんだにな～ぁれ
murachi@ubuntu-vbox:~/otoco/trunk/sample$ 
</pre>
<p>環境によっては libiconv を別途導入してコンパイルオプションに <code>-liconv</code> を付け加える必要があるかもしれません (MinGW とか←動作未確認)。</p>
<h3>正規表現を使いたい</h3>
<p>さて、上記のサンプルでさらっと <code>u32string</code> とか使っちゃってますが、このシノニムはドラフトの 21.3 にてちゃんと明記された標準のものです。もちろん、 <code>u16string</code> というのも存在します (<code>u8string</code> は無いので、考慮されているのはアラインメントのみと考えるべきですが…)。</p>
<p>しかし、「28 Regular expressions library」の章においては、 <code>char32_t</code> という文字はカケラも hit しません。標準の <code>&lt;regex&gt;</code> においては、 <code>char16_t</code>、 <code>char32_t</code> への対応は見送られてしまっているようです。</p>
<p>もちろん、<code>basic_regex</code> はテンプレートクラスなのですから、自分でテンプレートパラメータを指定してあげればうまくいきそうに見えます。しかし、<a href="http://blog.harapeko.jp/2010/09/22/u32regex-bad-cast/" title="はらぺこ日誌? ブログアーカイブ ? char32_t だと regex が使えない">同様の試みを Boost.Regex について行った際には、 <code>std::bad_cast</code> 例外が送出されてプログラムがエラー終了してしまいました</a>。将来的には、あるいは処理系によってはうまく動かせる (ようになる) のかもしれませんが、あまり期待は持たない方が良いかもしれません…。</p>
<p># そもそも <a href="http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.200x" title="C++ 200x - Chapter 1. Status (libstdc++)">GCC (libstdc++) では <code>&lt;regex&gt;</code> 自体がまだちゃんと実装されてなかったり</a>… orz</p>
<p>もっとも、<a href="http://blog.harapeko.jp/2011/09/21/boostregex-icu-with-char32_t/" title="はらぺこ日誌? ブログアーカイブ ? Boost.Regex の ICU 拡張と char32_t は相性がいいかも?">Boost.Regex の ICU 拡張における <code>UChar32</code> と <code>char32_t</code> (およびそれらの配列へのポインタ) を無理矢理キャストして使うと割と上手く行くっぽかったりする</a>ので、どうにかこうにかラッパーを書いて当座はそれで凌ぐというのも手かもしれません…。</p>
<p>ちなみに、<code>char</code> と UTF-8 を使用するのであれば <code>&lt;regex&gt;</code> はそのまま使えるはずですが、その場合、 (Boost.Regex と同様に) <a href="http://blog.harapeko.jp/2010/09/22/utf-8-is-nice/" title="はらぺこ日誌? ブログアーカイブ ? UTF-8 もイマイチだが…"><code>&lt;regex&gt;</code> は UTF-8 を知らないので、マルチバイト特有の問題に悩まされることになる</a>でしょう。少なくとも日本語の文字に対する量指定子 (<q><code>あ+</code></q> とか <q><code>あ?</code></q> とか) は期待通りには動きません。</p>
<p>仮に、<code>&lt;regex&gt;</code> が <code>char32_t</code> で利用できる場合、先のサンプルは以下のようなコーディングになるでしょう。こういう風に組める日がいつか来るといいですね… <tt>(;_;)/</tt>。</p>
<pre>
#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;string&gt;
#include &lt;regex&gt;

#include &quot;EncodeString.hpp&quot;

using namespace std;

typedef basic_regex&lt;char32_t, regex_traits&lt;char32_t&gt;&gt; u32regex;
typedef match_results&lt;u32string::const_iterator&gt; u32smatch;


int main()
{
    u32string before = U&quot;てくまくまやこんてくまくまやこん むらやましゃちょうよ おおきなくまにな～ぁれ&quot;;
    u32string after;
    u32regex reg(U&quot;くま&quot;);
    u32smatch match;

    u32string textbuf = before;
    while (regex_search(textbuf, match, reg)) {
        after += match.prefix().str() + U&quot;ぱんだ&quot;;
        textbuf = match.suffix().str();
    }
    after += textbuf;

    cout &lt;&lt; &quot;before: &quot; &lt;&lt; EncodeString&lt;char32_t, chset_Utf32&gt;(before, chset_Utf8).getCharArray() &lt;&lt; endl;
    cout &lt;&lt; &quot;after: &quot; &lt;&lt; EncodeString&lt;char32_t, chset_Utf32&gt;(after, chset_Utf8).getCharArray() &lt;&lt; endl;

    return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2011/12/27/cpp11-unicode/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Boost.Regex の ICU 拡張と char32_t は相性がいいかも?</title>
		<link>https://blog.harapeko.jp/2011/09/21/boostregex-icu-with-char32_t/</link>
		<comments>https://blog.harapeko.jp/2011/09/21/boostregex-icu-with-char32_t/#comments</comments>
		<pubDate>Wed, 21 Sep 2011 02:27:05 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[Boost]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[C++0x]]></category>
		<category><![CDATA[Unicode]]></category>
		<category><![CDATA[文字列処理]]></category>
		<category><![CDATA[正規表現]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=205</guid>
		<description><![CDATA[なんとなく Virtual Box から利用している Ubuntu のアップグレードなどをして、そこからなんと [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>なんとなく Virtual Box から利用している Ubuntu のアップグレードなどをして、そこからなんとなく「やっぱり Long Time Release 版の Ubuntu もテスト環境に持っておきたいよなぁ」などと思いつつ Virtual Box ディスクイメージを追加でこさえて、 Boost ライブラリのセットアップなどもしつつ動作チェックも兼ねて<a href="http://blog.harapeko.jp/2010/09/22/u32regex-bad-cast/" title="はらぺこ日誌» ブログアーカイブ » char32_t だと regex が使えない">昔書いた記事</a>なんぞを掘り起こしておりましたら、そこに書かれた内容に関連して、そういえば Boost.Regex も ICU ライブラリと組み合わせれば Unicode に対応できたはずだよなぁなどということが気になりだしてしまいまして、いろいろ試しているうちに、以下のようなサンプルコードが問題なく動作してしまうことを発見してしまったのでメモしておこうかと思った次第なのであります。ああ、なんだかこちらのブログも口調が個人ブログや普段の Twitter とかでのそれに似てきてしまいました <tt>(^_^;A</tt> 。<br />
<span id="more-205"></span></p>
<pre>
#include &lt;string&gt;
#include &lt;iostream&gt;
#include &lt;boost/regex.hpp&gt;
#include &lt;boost/regex/icu.hpp&gt;

using namespace std;

using boost::u32regex;
using boost::u32match;

int main()
{
        u32string text(U&quot;C++0x のせかいへようこそ!!&quot;);
        cout &lt;&lt; &quot;pre-modified text length = &quot; &lt;&lt; text.length() &lt;&lt; endl;
        u32string modified;
        u32regex reg(reinterpret_cast&lt;UChar32 const*&gt;(U&quot;せかい&quot;));
        u32match match;
        while (boost::regex_search(reinterpret_cast&lt;UChar32 const*&gt;(text.c_str()), match, reg)) {
                modified += u32string(reinterpret_cast&lt;char32_t const*&gt;(match.prefix().str().c_str())) + U&quot;世界&quot;;
                text = reinterpret_cast&lt;char32_t const*&gt;(match.suffix().str().c_str());
        }
        modified += text;
        cout &lt;&lt; &quot;modified text length = &quot; &lt;&lt; modified.length() &lt;&lt; endl;
        return 0;
}
</pre>
<p>とりあえず動作確認環境は以下の通りです。</p>
<ul>
<li>Ubuntu 11.04 + gcc 4.5.2 + Boost 1.42.0</li>
<li>Ubuntu 10.04 LTS + gcc 4.4.3 + Boost 1.40.0</li>
</ul>
<p>どちらでもコンパイルコマンドは以下で通ります (ソースファイルを <code>u32test.cpp</code> として保存した場合)。</p>
<pre>
$ g++ -std=c++0x -o u32test u32test.cpp -lboost_regex
</pre>
<p>実行してみると、置換前と置換後の文字数が正しくカウントされており、マッチングが期待通りに動作していることが確認できます。</p>
<pre>
$ ./u32test
pre-modified text length = 17
modified text length = 16
$ 
</pre>
<p>ただ、コードを見ていただければわかる通り、 <code>reinterpret_cast</code> の嵐であり、こうした書き方が C++0x 的にも Boost.Regex 的にも ICU 的にも Valid なのかはわかりません。また、現時点では Windows 環境 (MinGW + gcc 4.5 など) での動作確認は行っておりません。 ICU 拡張部分のヘッダを見る限り、内部で <code>wchar_t</code> を使っているので、 <code>wchar_t</code> が 16bits 境界になっている Windows では、バイトオーダーがひっくり返るなどの問題があって、もしかしたら正常に動かないかもしれません。</p>
<p>あくまで参考までと言いますか、将来的にはこういう感じの書き方ができるようになると良いなぁと言う程度の妄想、と捉えていただければと思います… <tt>m(_ _)m</tt></p>
<p>ちなみに、ここ最近ちっとも進んでいない otoco ですが、恐らく C++0x 自体の採用を見送る形になると思われます…。ただ、 Boost.Regex の ICU 拡張は有効利用できそうな気がしてきたので、こちらは利用することになるかもしれません。</p>
<p>Google の <a href="http://code.google.com/p/re2/">re2</a> に流れてしまいそうな悪寒もしてますが…。</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2011/09/21/boostregex-icu-with-char32_t/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>UTF-8 もイマイチだが…</title>
		<link>https://blog.harapeko.jp/2010/09/22/utf-8-is-nice/</link>
		<comments>https://blog.harapeko.jp/2010/09/22/utf-8-is-nice/#comments</comments>
		<pubDate>Wed, 22 Sep 2010 14:55:16 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[Boost]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[C++0x]]></category>
		<category><![CDATA[otoco]]></category>
		<category><![CDATA[Unicode]]></category>
		<category><![CDATA[文字列処理]]></category>
		<category><![CDATA[正規表現]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=138</guid>
		<description><![CDATA[UTF-32 が内部文字列に使えないことがわかったので、 UTF-8 を内部文字列に使用するというルールで l [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><a href="http://blog.harapeko.jp/2010/09/22/u32regex-bad-cast/" title="はらぺこ日誌» ブログアーカイブ » char32_t だと regex が使えない">UTF-32 が内部文字列に使えないことがわかった</a>ので、 UTF-8 を内部文字列に使用するというルールで libiconv によるエンコーディング操作と Boost.Regex による正規表現の両方を同時に試すサンプルを作成してみました。</p>
<ul>
<li><a href="http://developer.harapeko.jp/trac/original/otoco/browser/trunk/sample/EncodeString.hpp">sample/EncodeString.hpp</a></li>
<li><a href="http://developer.harapeko.jp/trac/original/otoco/browser/trunk/sample/EncodeString.cpp">sample/EncodeString.cpp</a></li>
<li><a href="http://developer.harapeko.jp/trac/original/otoco/browser/trunk/sample/regex-test.cpp">sample/regex-test.cpp</a></li>
</ul>
<p>Makefile は作ってません＼(^O^)／。試してみたい人は頑張ってコンパイルしてねｗ</p>
<pre>
$ g++ -std=c++0x -o regex-test regex-test.cpp EncodeString.cpp -lboost_regex
</pre>
<p>まともな環境 (Linux + GCC4.5 とか) なら上記コマンドで通るはず。libiconv を (glibc に上書きする形で) インストールしている場合は <code>-liconv</code> を末尾に入れる必要があるかも。そして MinGW を使う場合は更にもう一工夫必要かも <tt>(((;/^^)/</tt><br />
<span id="more-138"></span><br />
さてこのプログラム、注目して頂きたいのは、<code>regex-test.cpp</code> の以下の行です。</p>
<pre>
        regex reg(u8"くま|川|(お)?魚");
</pre>
<p>正規表現を定義しているのですが、一文字でしかないはずの <code>"お"</code> がわざわざ丸括弧でくくってありますね。これが Perl で <code>use utf8;</code> していたり、 UTF-8 で JavaScript を書いていたりしているのであれば、不要な括弧です。</p>
<p>しかし、 Boost.Regex を UTF-8 で使用する場合には必要です。この括弧がなければ、 <code>"?"</code> は <code>"お"</code> の最終オクテットにしか適用されません。なぜなら、 Boost.Regex は UTF-8 なんて知らないので、 <code>"お"</code> が論理的には 1文字でしかない、なんてことは認識できないからです。</p>
<p>すなわち、</p>
<pre>
        regex reg(u8"くま|川|お?魚");
</pre>
<p>と書いてしまうと、<code>"お魚"</code> には hit しますが、 <code>"魚"</code> には hit しなくなってしまうのです。</p>
<p>otoco では、 MML コンパイラ機能において、プログラマブルマクロを定義できる機能を提供する予定です。具体的には Lua でマクロを定義し、そのマクロを用いて生成した MML をインライン展開できるようにする、といったものです (実際の所、言語に Lua を採用すべきか JavaScript を採用すべきか、はたまた Python を採用すべきかで迷っているところではあるのですが…)。</p>
<p>で、 (Lua を採用する場合には) 正規表現を用いた文字列加工を行う関数を提供するつもりでいるのですが (JavaScript とかだったら不要なんですけどね、言語機能にあるし)、仮に非 ASCII な文字 (列) を hit させようとする正規表現に <code>"?"</code> やら <code>"[</code>…<code>]"</code> やらが使われた場合、どう扱うべきなんだろう、といった辺りが悩みどころではあったりします…。</p>
<p>まぁなんにせよ、他に選択の余地もないので、とりあえず内部文字列は UTF-8 で実装するという方針でやっていくことにしようかと思います。前途多難じゃ… orz</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2010/09/22/utf-8-is-nice/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>libiconv で文字セット自動認識</title>
		<link>https://blog.harapeko.jp/2010/03/03/libiconv-gues/</link>
		<comments>https://blog.harapeko.jp/2010/03/03/libiconv-gues/#comments</comments>
		<pubDate>Wed, 03 Mar 2010 09:02:48 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[活動記録]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[iconv]]></category>
		<category><![CDATA[otoco]]></category>
		<category><![CDATA[Unicode]]></category>
		<category><![CDATA[文字セット]]></category>
		<category><![CDATA[文字列処理]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=111</guid>
		<description><![CDATA[ご無沙汰ぶりです…。 以前、wchar_t はどうにも使い物にならないからどうしよう、といった記事を書いたので [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>ご無沙汰ぶりです…。</p>
<p>以前、<a href="http://blog.harapeko.jp/2009/07/25/wchar_t-suck/" title="はらぺこ日誌» ブログアーカイブ » 頼りなさげな wchar_t">wchar_t はどうにも使い物にならないからどうしよう</a>、といった記事を書いたのですが、その続きのお話です。</p>
<p>表題の通りで、 libiconv を用いて文字セットを自動認識する処理のサンプルを書いてみました。詳しい経緯は<a href="http://developer.harapeko.jp/trac/original/otoco/ticket/4" title="#4 (テキストファイルから読み込んだ文字列を wchar_t 配列と Unicode で扱う方法を調査する。) – otoco">Ticket 内で逐次コメントしています</a>。<br />
<span id="more-111"></span><br />
<a href="http://developer.harapeko.jp/trac/original/otoco/browser/trunk/sample/guessutf8.cpp" title="/trunk/sample/guessutf8.cpp – otoco">これ</a>がそのサンプルプログラムです。このプログラムは、</p>
<ol>
<li>標準入力からファイルを読み込み、</li>
<li>ファイルの文字セットを自動認識し、</li>
<li>句点「。」をピリオド「.」に、読点「、」をカンマ「,」に置換し、</li>
<li>UTF-8 に変換して標準出力に書き出す。</li>
</ol>
<p>ということをやるものです。</p>
<p>で、<a href="http://blog.harapeko.jp/2009/07/25/wchar_t-suck/" title="はらぺこ日誌» ブログアーカイブ » 頼りなさげな wchar_t">以前のブログ記事</a>では、</p>
<blockquote cite="http://blog.harapeko.jp/2009/07/25/wchar_t-suck/" title="はらぺこ日誌» ブログアーカイブ » 頼りなさげな wchar_t">
<p>というわけで、内部コードは wchar_t のような型名で定義するのではなく、より具体的に文字セットで定義した方が良さそうだなぁという結論に至りました。候補は以下の 2通りです。</p>
<ul>
<li>UCS4 を内部コードとし、物理型は符号無し 32bits 整数を適当な型名に typedef して用いる。</li>
<li>UTF-8 を内部コードとし、物理型は char を用いる。</li>
</ul>
</blockquote>
<p>と書いておりましたが、今回はこのうち、前者の UCS4 を内部コードとして用いる方法で実装しています。</p>
<p>とりあえず動いたから commit してみた、という段階なので、コメントが不十分だったり魔法の値が散らばっていたりと未熟なコードです。追々直していこうかと思っています。また、後者の UTF-8 を内部コードとして用いる方法についても併せて書いてみる予定です。</p>
<p>また、現状では boost::regex を用いたコードにはなっていないので (1文字ずつの置換なので UCS4 だと使う必要がないのよ、今のところ)、これを用いた形に修正した場合、どうなるか、といった辺りも試していきたいと思っています。実際にコードに起こしてみると、頭で分かっている以上の利点や問題点が分かってくるんじゃないかなと。</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2010/03/03/libiconv-gues/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>頼りなさげな wchar_t</title>
		<link>https://blog.harapeko.jp/2009/07/25/wchar_t-suck/</link>
		<comments>https://blog.harapeko.jp/2009/07/25/wchar_t-suck/#comments</comments>
		<pubDate>Sat, 25 Jul 2009 09:42:17 +0000</pubDate>
		<dc:creator><![CDATA[村山 俊之]]></dc:creator>
				<category><![CDATA[技術メモ]]></category>
		<category><![CDATA[活動記録]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[iconv]]></category>
		<category><![CDATA[otoco]]></category>
		<category><![CDATA[Unicode]]></category>
		<category><![CDATA[wchar_t]]></category>
		<category><![CDATA[文字セット]]></category>
		<category><![CDATA[文字列処理]]></category>

		<guid isPermaLink="false">http://blog.harapeko.jp/?p=96</guid>
		<description><![CDATA[otoco に限らず、 PC 上で動作するプログラムの多くは、テキストを処理することを目的の一部またはすべてと [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>otoco に限らず、 PC 上で動作するプログラムの多くは、テキストを処理することを目的の一部またはすべてとしています。 otoco の場合は特に、どこの誰とも分からない人が MML を書き、それを読み込んで XML やら SMF やらオーディオやら楽譜やらに変換することを目的としているので、どこの誰が MML を (あるいは XML を直接) 書いても問題なく処理できるよう、文字セットの扱いには丁重でなければなりません。</p>
<p>当初の方針として、 otoco では内部コードに Unicode を使用し、その物理型は wchar_t で扱うつもりでいました。この辺、C/C++ でのクロスプラットフォーム開発に慣れていないと陥りやすい罠であるように思うのですが… 現状の wchar_t ははっきり言ってクロスプラットフォーム開発には向いていないものといわざるを得ないようです。</p>
<p>とりあえず確認しているのは Windows の VC++ 2008 と Linux の gcc だけなのですが、それだけでも調べた限りで以下のような相違点がありました。</p>
<p><span id="more-96"></span></p>
<table rules="all">
<tr>
<th>開発環境</th>
<th>文字セット</th>
<th>物理型</th>
</tr>
<tr>
<td>Windows + MS-VC++ 2008</td>
<td>UTF-16LE</td>
<td>符号無し 16bits 整数 (unsigned short)</td>
</tr>
<tr>
<td>Linux + gcc</td>
<td>UCS4</td>
<td>符号無し 32bits 整数 (uint32_t)</td>
</tr>
</table>
<p>まず文字セットですが、 UTF-16LE とはリトルエンディアンの UTF-16 エンコードのことで、 Unicode を表現するための<strong>ファイル形式</strong>です。ファイル形式であるということは、すなわちファイルに保存する方法を定めた形式であるということです。それに対して、 UCS4 はあくまで Unicode そのものであり、内部データ形式として扱えるものです。</p>
<p>具体的に何が違うのかというと、 UTF-16 の場合は配列内の数値 1つが必ず 1文字を表現するものであることを保証しません。実際、UTF-16 ではサロゲートペアを気にする必要があり、この処理を誤ると文字境界に破綻を来して文字化けの原因を作ってしまうことになります。これに対し、 UCS4 の場合は単に 31bits 以下の文字セットであり、それより拡張されないことが保証されています (万一拡張された場合は新たに UCS8 が規定されて包括されるのでしょうが、現実的にはあり得ないでしょう)。</p>
<p>私は元より Windows 畑の人なので、 wchar_t を使う場合でもサロゲートペアをどうにかすることを前提に考えていましたから、 GNU/Linux でのあり方はむしろ理想的とも思うのですが、反面内部的な処理に過ぎない部分でプラットフォーム依存を気にしながら実装しなければならないというのはあまり好ましいことではなく、そう考えると wchar_t という型は意味論的には破綻しているといわざるを得ないように思います。さらに BSD 系の UNIX 環境では wchar_t が扱う文字セットは環境のロケールに依存するなどという情報もあり… とてもじゃないですがそんなの考慮しきれるわけがありません ((((/;^^)/ 。</p>
<p>というわけで、内部コードは wchar_t のような型名で定義するのではなく、より具体的に文字セットで定義した方が良さそうだなぁという結論に至りました。候補は以下の 2通りです。</p>
<ul>
<li>UCS4 を内部コードとし、物理型は符号無し 32bits 整数を適当な型名に typedef して用いる。</li>
<li>UTF-8 を内部コードとし、物理型は char を用いる。</li>
</ul>
<p>前者のメリットは何と言っても多言語処理の確実性が高く、文字境界も気にする必要がないことです。例えば、配列の中の n個目の値は、確実に文字列の中の n個目の文字であることが保証されます。反面、 STL や Boost を用いた文字列処理においては、あらかじめ typedef された便利な型名を用いることができず、プログラム側で内部コード用に typedef したものをたくさん用意しておく必要が生じるでしょう。また、何より文字列リテラルが使えなくなるので、正規表現のハードコーディングには工夫を強いられることになります。</p>
<p>後者のメリットは STL の string や Boost.Regex に定義されている typedef がそのまま利用できること、そして何よりハードコーディングした文字列リテラルがそのまま利用できることです。正規表現の記述もこちらの方がよっぽどすっきりするでしょう。また、 XML の入出力を UTF-8 に限定して良いのであれば、その辺の実装も楽になるかも知れません。文字境界については注意する必要がありますが、例えば n文字目の検出は他のエンコーディングに比べれば容易であるのも UTF-8 の特徴でもあります (もちろん、UCS4 を用いる場合に比べれば、実装は複雑になりますが…)。</p>
<p>ちなみに、文字セットの変換にはやっぱり iconv を使うことになりそうです。 Windows 側はまだ試していないのですが… とりあえず近日中に iconv を用いた簡単なプロトタイプを書いて、上記の件も含めて検討してみる予定です…。</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.harapeko.jp/2009/07/25/wchar_t-suck/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
