= 文字列

人類史が始まって以来、情報社会といわれる今日まで、
情報のほとんどは文字として表現されてきました。
文字列処理に優れた言語が「使いやすい」のは、
文字というものが我々が処理する情報のもっとも根幹をなしているからでしょう。

== 文字と文字コード

プログラミング言語は、その大半が海外で生まれたという経緯もありまして、
長らく「文字＝ASCIIコード＝1バイト」という暗黙の呪縛がありました。
これが不幸の始まりで、低レベルな処理を意識しないで済むはずのスクリプティング言語でも、
日本語を処理するときは「１文字が何バイトに相当するか」と注意する必要がありました。

Konoha は、幸運なことに、多国語処理の経験が蓄積された21世紀になってから設計されたため、
混乱なく、1文字は「何バイトでエンコーディングされていようとも1文字」というスタンスです。
１文字は、２バイトで足りるかという論争にも無縁ですし、
文字単体を扱う char 型のような型も存在しません。

文字は、長さ1の文字列として表現されます。

{{{
"a"            　// ASCII文字
"あ"            // Unicode 文字
}}}

現在の Konoha の実装では、UTF8 をそのまま


どうしても文字コードを扱う必要があるときは、UCS4(31ビット)コードとして、Int}型を用いて扱うことができます。

{{{
>>> "a".getChar()         // ASCIIコード
97
>>> "あ".getChar()        // UCS4 コード
12354
}}}

Konoha は、String}の内部エンコーディングとして、UTF8を利用しています。本来、文字列は実装依存しない抽象化されたデータ構造であるべきであるが、UTF8以外はバイト列(Bytes}/{¥tt byte[]})して扱うことになります。String}から、各種エンコーディングにしたがってバイト列に変換する方法も用意されています。

{{{
s.getBytes("shift_jis")  // Shift_JIS へ変換
new String(byte, "shift_jis");
}}}

注意：入出力ストリームにはエンコーディングが設定できるようになっているため、エンコーディングを処理するケースはあまりない。


== 文字列と演算子}

文字列は、「文字」配列です。ただし、Konoha のString}クラスは、不変(Immutable)オブジェクトとして導入されているため、読み込み専用の「文字」配列となります。

=== 長さとシーケンス}

文字列は、0個以上の文字が並んだシーケンスです。Konohaでは、シーケンス s は、¥verb#|s|# 演算子を用いることで、その長さを得ることができます。文字列の場合、そこに含まれる文字の数です。

{{{
>>> s = "";　　
>>> t = "ABC"; 
>>> u = "ABCいろは";
>>> |s|
0
>>> |t|
3
>>> |u|
6
}}}

Konohaの文字列の長さは、多国語化された設計となっているため、文字のバイト数に関わらず、文字数が文字列の長さとなります。ASCII(1バイト)文字列を前提としたバイト数でないことに注意したい。もし文字列のバイト数をしりたい場合は、一旦、バイト配列(byte[]|)に変換すればよい。ただし、このときUTF8エンコーディングが用いられ、それにしたがい日本語は3バイト以上になっています。

{{{
>>> s = "ABCいろは";
>>> b = (byte[])s;        // バイト配列へ変換
>>> |b|
12
}}}

文字列は、文字の配列として扱うことができます。配列のインデックスは、0から始まり、¥verb#|s| - 1# です。

{{{
>>> s = "ABCいろは";
>>> s[0]
"A"
>>> s[|s|-1]            // 最後の一文字
"は"
}}}

注意：Konoha の文字列は、変更不能(immutable)です。通常の配列のように値を変更することができない。

文字列から部分文字列(substring)を取り出すときは、シーケンススライス演算子を用いることができます。

{{{
>>> s = "ABCいろは";
>>> s[..2]
"ABC"
>>> s[3..]
"いろは"
>>> s[2..4]
"Cいろ"
}}}

=== 連結}

文字列の連結は、Java と同様に、加算(+)演算子によって書くことができます。この加算は、もちろん結合則や交換則に基づくものではないが、操作が直感的であり、広く使われています。

{{{
>>> "Uzumaki" + " " + "Naruto"
"Uzumaki Naruto"
}}}

Konoha は、文字列の加算を最も優先されるオペレータとして定義しています。つまり、文字列と文字列以外のオブジェクトを混在して換算した場合、全て文字列に変換されたのち連結されます。

{{{
>>> "Naruto " + 8 + 1            // %s(8) + %s(1)と同じ
"Naruto 81"
}}}

もし数値演算を優先したい場合は、括弧()で囲んで先に計算する。

{{{
>>> "Naruto " + (8 + 1)          // %s(8 + 1)と同じ
"Naruto 9"
}}}

=== 連結、除去、分割}

Konohaでは、加算に加えて、減算、乗算、除算の演算子にもそれぞれ各演算子の意味に近い文字列操作として定義されています。これらも数学的な性質は継承しないが、文字列処理としてときに便利です。

文字列の減算 s - t| は、文字列 s から指定された文字列 t を取り除く操作です。これは、s.replace(t, "")|に相当する。

{{{
>>> "Uzumaki Naruto" - "a" - "u";
"Uzmki, Nrto"
}}}

文字列の乗算s * n|は、文字列の四則演算の中で唯一整数値を引数にとる。しかし、そこから得られる結果は極めて直感的に、文字列s を n個連結したものです。

{{{
>>> "Naruto" * 3;
"NarutoNarutoNaruto"
>>> "hello" * 0
""
}}}

文字列の除算s / t|は、文字列 s を文字列tで分割したときの先頭の文字列です。文字列の余算s % t|は、分割された残りの文字列となります。
これは、s.split(t)|の簡易版として利用されます。

{{{
>>> "Uzumaki Naruto" / " "
"Uzumaki"
>>> "Uzumaki Naruto" % " "            // mod のこと
"Naruto"
}}}

=== 文字列の比較}

文字列は、関係演算子によって比較することができます。そのとき、単純にアルファベットオーダー(UTF8 のコード順)で比較され、英大文字/英小文字は無視されない。

{{{
>>> "Uzumaki" == "Naruto"
false
>>> "Uzumaki" > "Naruto"
ture
}}}

== 文字列メソッド}

Konohaは、Javaに由来するString}クラスのメソッドを（一部の例外はあるが）そのままサポートしています。また、より多くの文字列操作メソッドも拡張しています。本節でとりあげるメソッドは、その一部です。しかし、本節のメソッドだけで、KonohaプロジェクトでKonoha言語のエンジンのソースコードを生成しているドメイン言語(DSL)を十分に作成することができます。

=== equals()}

Java は、少々、プログラマには不便な特徴であったが、==|演算子をオブジェクトの参照（リファレンス）の比較演算子として定義していた。そのため、文字列s,t|の値を比較するとき、s.equals(t)|のような特別なメソッドを利用していた。

Konoha は、同じ文字列同士であれば、==|, !=|, <|, <=|, >|, >=|演算子を用いて比較することができます。これはより直感的なプログラミングを可能にしているが、Java とちょっとした互換性を高めるため、同様にs.equals(t)|メソッドも導入しています。

{{{
>>> s = "naruto";
>>> s.equals("naruto");     //  == 演算子と同じ 
true
>>> s == "naruto"
ture
}}}

¥subsubsection{equals:IgnoreCase()}

equals()|は、英大文字/小文字を区別する。:IgnoreCase|バリエーションを利用すれば、英大文字/英小文字を無視してマッチングを行うことができます。

{{{
>>> s = "Uzumaki"
>>> s.equals("uzumaki")
false
>>> s.equals:IgnoreCase("uzumaki")
true
}}}

これは、Java のequalsIgnoreCase()|と同じです。Konoha では、独自のメソッド・バージョン管理を利用して、equals()|のバリエーションを管理しています。:IgnoreCase| バリエーションは、equals()|以外にも、startsWtih()|, endsWith()|, indexOf()|, lastIndexOf()|などに用意されています。

=== startsWith(), endsWith()}

Konoha は、正規表現によるパターンマッチングが利用できるが、多くの場合、文字列の先頭、もしくは末尾のみマッチングするだけで事足りることが多い。メソッドs.startsWith(t)|は、文字列 s|の先頭が文字列t|と一致するか判定するときに利用できます。逆に、メソッドs.endsWith(t)|は、文字列 s|の末尾が文字列t|と一致するか判定できます。

{{{
>>> s = "Uzumaki Naruto"
>>> s.startsWith("Uzumaki")
true
>>> s.startsWith("Naruto")
false
>>> s.endsWith("Uzumaki")
false
>>> s.endsWith("Naruto")
true
}}}

startsWith()|とendsWith()|は、英大文字/小文字を区別する。equals()|と同様に:IgnoreCase|バージョンを利用すれば、英大文字/英小文字を無視してマッチングを行うことができます。

{{{
>>> s = "Uzumaki Naruto"
>>> s.startsWith("uzumaki")
false
>>> s.startsWith:IgnoreCase("uzumaki")
true
}}}

=== 検索: indexOf(), lastIndexOf()}

関数index()|系の文字列操作は、C言語の標準ライブラリから存在する古典的な文字列ライブラリです。Konohaでは、Java同様に、文字でなく、部分文字列の検索ができます。

メソッドs.indexOf(t)|は、文字列 s|の先頭から部分文字列t|を探し、その位置を返すメソッドです。逆に、メソッドs.lastIndexOf(t)|は、文字列 s|の末尾から部分文字列t|を探し、その位置を返すメソッドです。位置は、文字列のインデックスと同様に0から始まる。ともに、部分文字列t|を発見できなかった場合は、-1| を返す。

{{{
>>> s = "Uzumaki Naruto"
>>> s.indexOf("Naruto")
>>> s.indexOf("Sakura")
-1
>>> s.indexOf("a")
4
>>> s.lastIndexOf("a")
9
}}}

最もよく使う利用法は、シーケンススライスと組み合わせて、文字列を分割するときです。

{{{
>>> s = "Name: Uzumaki Naruto"
>>> s[.. s.indexOf(": ")]
"Name"
>>> s[s.indexOf(": ")+1 ..].trim()
"Uzumaki Naruto"
}}}

=== split(), tokenize()}

メソッドs.split(t)|は、文字列 s|を分割文字列t|によって単純に分割するメソッドです。分割された文字列は、String[]|に格納されます。（分割文字列t|)は、含まれない。）

{{{
>>> s = "Naruto, Sakura, Sasuke"
>>> s.split(", ")
["Naruto", "Sakura", "Sasuke"]
>>> s.split(",")
["Naruto", " Sakura", " Sasuke"]
}}}

特別な用例としては、分割文字列を省略すると、文字列は1文字ずつsplit()されます。

{{{
>>> s = "naruto"
>>> s.split()
["n", "a", "r", "u", "t", "o"]
}}}

もう少し意味のある単位でトークンを分割したい場合は、メソッドs.tokenize()|も利用できます。これは、文字列 s|を Konoha のレキシカル構造にしたがってトークンに分割するメソッドです。

{{{
>>> s = '''
print (1+1), 'hello,world';
'''
>>> s.tokenize()
["print", "(", "1", "+", "1", ")", 
   ",", "'hello,world'", ";"]
}}}

=== 置換: replace()}

メソッドs.replace(t, u)|は、文字列 s|中に出現する部分文字列t|を与えられた文字列u|によって置き換えるメソッドです。Konohaでは、文字列は不変オブジェクトであり、破壊的操作はできないため、新しいString オブジェクトが生成され、メソッドの戻り値として返されます。

{{{
>>> s = "Haruno Naruto"
>>> t = s.replace("Haruno", "Uzumaki")
>>> t
"Uzumaki Naruto"
>>> s                             // 元の文字列は変わらない
"Haruno Naruto"
}}}

注意：replace()|メソッドは、正規表現パターンによる置き換えも可能です。詳しくは、「第¥ref{regular_expression}節 正規表現」で述べます。

=== toUpper(), toLower()}

メソッドs.toUpper()|は、文字列 s|の英文字を英大文字に変換し、メソッドs.toUpper()|は、文字列 s|の英文字を英小文字に変換するメソッドです。

{{{
>>> s = "Uzumaki Naruto"
>>> s.toUpper()
"UZUMAKI NARUTO"
>>> s.toLower()
"uzumaki naruto"
}}}

注意： Konohaは、メソッド名も含めて、できるだけJavaクラスライブラリとの互換性を意識しているが、本メソッドのみはどうしてもtoUpperCase()|, toLowerCase()|という名前にできなかった。String}クラスの数少ない例外です。

=== メソッドのパッケージ拡張*}

Konoha は、実行中であってもクラスに新しいメソッドを追加したり、既存の振る舞いを変更することができます。String} クラスも例外ではなく、パッケージのインポートによって、メソッドが拡張されることがあります。

次は、japanese パッケージをインポートしたことで、新しいメソッドが追加されたり、既存のメソッドが日本語処理に対応している例です。

{{{
>>> using japanese;                 // 日本語パッケージ
>>> ("なると").toKatakana()         // カタカナへの変換
"ナルト"
>>> ("ナルト　").trim()             // 全角空白も除去される
"ナルト"
}}}

== フォーマッティング}

フォーマッタは、オブジェクトを様々な書式で文字列化するKonoha独自の機能です。理想的には、全てのオブジェクトはクラス階層にしたがってフォーマッタが定義され、全てのフォーマッティングはオブジェクト指向モデルによってよく再利用されるべきです。しかし、現実問題としては、再利用性を考慮に入れるよりも、とりあえずアドホックでもフォーマッティングができることが望ましいことが多い。

Konoha では、「テンプレート・フォーマッティング」と「インライン・フォーマッティング」の2種類のフォーマッティングを導入しています。どちらの場合も、定義済みのフォーマッタは、テンプレート書式の一部として用いることができます。

=== テンプレート・フォーマッティング}

テンプレート・フォーマッティングは、文字列としてテンプレートを作成し、そのテンプレートをビルトイン関数 format()|を用いることで、パラメータと組み合わせてフォーマットされた文字列を生成する仕組みです。

{{{
>>> format("We are at [%s{0}:%d{1}]:", __file__, __line__)
"We are at [(shell):1]"
}}}

テンプレートは、テンプレート書式にしたがえば、あとは通常の文字列として扱うことができます。次は、変数に格納されたテンプレートをフォーマット処理したときです。

{{{
>>> fmt = "We are at [%s{0}:%d{1}]:";
>>> s = format(fmt, __file__, __line__)
>>> s
"We are at [(shell):4]"
}}}

=== テンプレート書式}

テンプレート書式は、"%x{n}"|スタイルで書く。
これは、「フォーマッタ%x|に n 番目のパラメータの値を適用する」と意味になります。ここでインデックスは、0 から始まる。わざわざ、パラメータのインデックスを指定できる理由は、英語と日本語のような語順が違うテンプレートでも、パラメータの順番を気にしなくて適用するためです。

{{{
>>> fmt = "%s{1}内で%s{0}が見つかりません."
>>> fmt = "%{0}: Not found in %s{1}"
}}}

=== フォーマッティング関数}

テンプレートは、ビルトイン関数 format()|を用いて、フォーマッティングされます。フォーマットされた文字列は、関数の戻り値として返されます。

{{{
>>> format(fmt, "Class", "file.k")
"Class: Not found in file.k"
}}}

%(...)|は、format(...)|のショートカット名です。下の例は、上のフォーマッティングと全く同じです。

{{{
>>> %(fmt, "Class", "file.k")
"Class: Not found in file.k"
}}}

フォーマッティング時のパラメータは、Konohaにおいて数少ないコンパイル時に型検査されないパートです。フォーマッタは、与えられたパラメータのクラスに対して、動的にバインドされます。もし指定されたフォーマッタが定義されてなければ、自動的に %empty|が適用され、そこには文字列が表示されない。

もし厳密な型検査を求める場合は、関数等でラッピングして用いるとよい。

=== インライン・フォーマッティング}
¥label{inline_formatting}

Konoha では、バッククオートによる文字リテラルと、#|で始まるライン文字列リテラルに対してのみ、インデックスの代わりに変数やその他の式を直接埋め込むことができるインライン・フォーマッティングを認めています。

{{{
c.query 
   # select name, salary from PERSON_TBL
   # where age > %d{age} and age < %d{age2}
}}}

== 正規表現}
¥label{regular_expression}

正規表現(regular expression) は、文字列のパターンを記述する小さな言語です。与えられた正規表現パターンは、有限オートマトンにコンパイルされ、入力文字列に対して高速にマッチングされます。

スクリプティング言語では、Perl を代表例として文字列処理の中心的機能として正規表現を言語に統合しています。Konoha では、独自の「正規表現」という概念は存在しないが、GNU regex, Oniguruma, PCRE(Perl Compatible Regular Expression) など、既存の正規表現ライブラリを選択して利用することができます。

=== 正規表現リテラル}

正規表現パターンは、Regex} クラスのインスタンスとして表現されます。Konohaでは、Perl, Ruby, JavaScript などと同様に、専用の正規表現リテラルが用意されています。正規表現リテラルは、/|記号で囲まれた文字列であり、そこで表現された文字列は正規表現パターンと解釈されます。

{{{
pattern = /s$/
}}}

正規表現リテラルは、Regex}コンストラクタで生成されるオブジェクトと全く同じです。また、Regex コンストラクタは、@Const 属性であるため、コンパイル時にオブジェクトが生成されるため、性能の面でも同じです。しかし、正規表現リテラルの方を使うことが圧倒的に多い。何よりもシンプルに記述できるからです。特別なオプションを指定するときだけ、現在のところ、コンストラクタを使う必要があります。

{{{
pattern = new Regex("s$", "i");         // 英大文字/小文字を無視..
}}}

=== 正規表現エンジン}

Konoha は、標準的な正規表現エンジンとして、POSIX regex ライブラリを利用することが多い。現在の正規表現エンジンは、$konoha.regex| で確認することができます。

{{{
>>> $konoha.regex
"GNU regex library"
}}}

正規表現エンジンは、正規表現パッケージをインポートすることで切り替えることができます。主な正規表現パッケージは、以下のとおりです。

¥begin{itemize}
¥item oniguruma} --- kosako 氏が開発し、Ruby で正式採用されている高性能な正規表現ライブラリ
¥item pcre} --- Perl 互換の正規表現ライブラリ
¥end{itemize}

切り換えは、パッケージのインポートと同期しています。つまり、正規表現パッケージをインポートすれば、正規表現ドライバが入れ替えられ、それ以降の正規表現は新しいエンジンに切り替わる。

{{{
>>> using oniguruma;
>>> $konoha.regex
"oniguruma"
>>> pattern = /^*.:*.$/          // 以後は、鬼車を利用
}}}

注意：正規表現ライブラリをインポートする以前の正規表現オブジェクトは更新が反映されない。¥¥

また、正規表現ライブラリごとの異なる機能を明示的に使い分けたいときは、正規表現リテラルの代わりに、'regex:'|、'pcre:'|、'onig:'| のように、それぞれのライブラリを識別する意味タグを用いることで、正規表現を用いることができます。

{{{
using oniguruma;
s =‾ 'onig:^*.:*.$'; 　　　　// 鬼車でマッチング
s =‾ 'regex:^*.:*.$';        // regexでマッチング
}}}

=== 正規表現と演算子}

Konoha は、Perl 由来の正規表現マッチング演算子をサポートしています。しかし、JavaScriptもサポートしていないようだから、この演算子は中止しようかと悩んでいます。

{{{
>>> s = "name: naruto";
>>> s =‾ /^*.: *.$/
true
}}}

=== 正規表現とメソッド}

String} クラスは、いくつかのメソッドを正規表現パターンに対応させています。

s.serach(p)|は、文字列s|の先頭から、正規表現パターンp|でマッチング検索を行い、最初にマッチした文字列インデックスを返す。indexOf()|の正規表現版です。

{{{
>>> text.search(/ha/);
}}}

s.match(p)|は、文字列s|に対して、正規表現パターンp|でマッチングを行い、そのマッチング結果の文字列を返す。複数個マッチした場合は、全て返す。

{{{
>>> s = "1 + 2 = 3";
>>> s.match(/¥d/);
["1", "2", "3"]
}}}

また、正規表現パターンにおいてグルーピングを用いれば、マッチングした文字列に続いて、グルーピングされた部分文字列も得ることできます。

{{{
>>> url = "http://www.website.org/konoha";
>>> url.match(/(¥w+):¥/¥/([¥w.]+)¥/(¥S*)/);
["http://www.website.org/konoha", 
   "http", "www.website.org", "konoha"]
}}}

%s.split(p)|では、分割文字列の代わりに、正規表現パターンp|を用いて分割することができます。

s.replace(p, fmt)|は、文字列s|に対して、正規表現パターンp|でマッチングを行い、その結果をフォーマッティングfmt|によって置き換える演算子です。

{{{
>>> s = "1 + 2 = 3";
>>> s.replace(/(¥d)/, "%s{1}.0");
"1.0 + 2.0 = 3.0"
}}}

=== 正規表現とマッピング**}

最近のPerl互換の正規表現では、名前付きのグルーピングを提供しています。文字列を正規表現からパースして、直接オブジェクトへマッピングする機能を計画しています。

{{{
>>> Class Url {
...   String site;
...   String path;
... }
>>> url = "http://www.website.org/konoha";
>>> pattern = /(¥w+):¥/¥/(?site:[¥w.]+)¥/(?path:¥S*)/;
>>> u = (Url with pattern)url;
>>> u
{site: "www.website.org", path: "/kohoha"}
}}}
