Microsoft Visual Studio International Feature Pack 2.0: Yomigana Frameworkを使う 1 †
Yomigana Frameworkとは? †
以前.NETプログラミング研究の85,86,87,88号でMicrosoft Visual Studio International Pack 1.0を紹介しました。その後、幾つかの問題点が修正されたMicrosoft Visual Studio International Pack 1.0 SR1が公開され、さらにその拡張となるMicrosoft Visual Studio International Feature Pack 2.0が公開されました。
Microsoft Visual Studio International Feature Pack 2.0では、日本語関係の新しいライブラリとして、Yomigana Frameworkが追加されました。Yomigana Frameworkには、ルビを振る機能を提供するクラスライブラリや、ユーザーがIMEで入力した文字列の読み仮名を簡単に取得できるテキストボックスコントロールなどが含まれています。今回はこのYomigana Frameworkについて説明します。
ルビを振る †
まずはYomigana Frameworkを使ってルビを振る方法を紹介します。この場合、使用するDLLはYomiganaFramework.dllだけですので、YomiganaFramework.dllをプロジェクトの参照設定に追加しておいてください。DLLは、Microsoft Visual Studio International Feature Pack 2.0をインストールしたフォルダにある「YomiganaFramework」というフォルダの中にあります。デフォルトでは、「C:\Program Files\Microsoft Visual Studio International Feature Pack 2.0\YomiganaFramework」のようなパスになります。
なおYomigana Frameworkは、.NET Framework 3.5以上でしか使用できません。また、Client Profileでは使用できません。もしYomigana Frameworkを使ってエラーが出るようであれば、プロジェクトのプロパティの「対象のフレームワーク」を確認してみてください。
Wikipediaによると、ルビを振る方法には幾つかありますが、Yomigana Frameworkでできるのはこの内、「ユニコード上のルビ」と「JIS X 4052上のルビ」の「特殊記号による対象要素の指定」です。Yomigana Frameworkではその他に、ルビを括弧等で囲むこともできます。
Yomigana Frameworkでルビを振ると言っても、自動的に振られる訳ではありません。自分でルビとする文字列を用意しなければなりません。
早速ですが、簡単な例を一つ。「笑う門には福来る。」という文章に括弧で囲んだ読み仮名を付けて、「笑(わら)う門(かど)には福(ふく)来(きた)る。」という文字列を作成してみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| | Dim srcs As New System.Collections.Generic.List(Of String)() From { _
"笑", "う", "門", "には","福", "来", "る。" _
}
Dim anns As New System.Collections.Generic.List(Of String)() From { _
"わら", "", "かど", "", "ふく", "きた", "" _
}
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
srcs, anns)
Dim result As String = strAnns.ToString(Nothing, Nothing)
Console.WriteLine(result)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| | System.Collections.Generic.List<string> srcs =
new System.Collections.Generic.List<string>()
{ "笑", "う", "門", "には", "福", "来", "る。" };
System.Collections.Generic.List<string> anns =
new System.Collections.Generic.List<string>()
{ "わら", "", "かど", "", "ふく", "きた", "" };
Microsoft.International.Collections.StringAnnotations strAnns =
new Microsoft.International.Collections.StringAnnotations(srcs, anns);
string result = strAnns.ToString(null, null);
Console.WriteLine(result);
|
このようにまず、基になる文章をルビを振る節で分けたリスト(親文字のリスト)と、それに対応したルビのリストを用意します。この2つのリストからStringAnnotationsオブジェクトを作成します。
このように親文字のリストとルビのリストが別々だと、ルビがずれてしまうのが怖いですが、残念ながら親文字とルビをペアで登録できる方法はありません。ただ、ルビが振られた文字列からStringAnnotationsオブジェクトを作成する方法はあります。この方法については、後述します。
なお、ルビには文字列以外のオブジェクトを指定することもできるようです。
StringAnnotationsオブジェクトからルビが振られた文字列を取得するには、ToStringメソッドを使います。ToStringメソッドの2つのパラメータ両方にnullを指定すると、ルビが括弧で囲まれます。
StringAnnotationsクラスのプロパティ †
StringAnnotationsクラスには、幾つかのプロパティがあります。Sourceプロパティを使えば、基の文章を取得できます。Countプロパティでは、節の数を取得できます。インデクサであるItemプロパティでは、インデックス(節の位置)を指定して、ルビを取得、設定できます。
ところが何故か、親文字をインデックスで取得、設定できるプロパティ(あるいはメソッド)がありません。よって、Itemプロパティでルビを取得、設定できても、その親文字が分かりませんので、あまり役に立ちません。さらにStringAnnotationsクラスにはインデックスを指定して節をマージ、分割するメソッドもあるのですが、同様の理由で、非常に不便です。(自分で親文字のリストを管理すればできないことはありません。その方法は、ヘルプの「StringAnnotations の二つの節を結合する方法」や「StringAnnotations の節を二つに分割する方法」で説明されています。)
これらのプロパティを使った例を以下に示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| | Dim srcs As New System.Collections.Generic.List(Of String)() From { _
"笑", "う", "門", "には", "福", "来", "る。" _
}
Dim anns As New System.Collections.Generic.List(Of String)() From { _
"わら", "", "かど", "", "ふく", "きた", "" _
}
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
srcs, anns)
Console.WriteLine(strAnns.Source)
For i As Integer = 0 To strAnns.Count - 1
Console.Write(strAnns(i) + ",")
Next
Console.WriteLine()
For Each annotation As String In strAnns
Console.Write(annotation & ",")
Next
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| | System.Collections.Generic.List<string> srcs =
new System.Collections.Generic.List<string>()
{ "笑", "う", "門", "には", "福", "来", "る。" };
System.Collections.Generic.List<string> anns =
new System.Collections.Generic.List<string>()
{ "わら", "", "かど", "", "ふく", "きた", "" };
Microsoft.International.Collections.StringAnnotations strAnns =
new Microsoft.International.Collections.StringAnnotations(srcs, anns);
Console.WriteLine(strAnns.Source);
for (int i = 0; i < strAnns.Count; i++)
{
Console.Write(strAnns[i] + ",");
}
Console.WriteLine();
foreach (string annotation in strAnns)
{
Console.Write(annotation + ",");
}
|
ユニコード上のルビを振る †
次は、StringAnnotationsオブジェクトからユニコード上のルビが振られた文字列を取得してみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| | Dim srcs As New System.Collections.Generic.List(Of String)() From { _
"笑", "う", "門", "には","福", "来", "る。" _
}
Dim anns As New System.Collections.Generic.List(Of String)() From { _
"わら", "", "かど", "", "ふく", "きた", "" _
}
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
srcs, anns)
Dim fp As New _
Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider()
Dim result As String = strAnns.ToString(Nothing, fp)
Console.WriteLine(result.Replace(Char.ConvertFromUtf32(&HFFF9), "\uFFF9") _
.Replace(Char.ConvertFromUtf32(&HFFFA), "\uFFFA") _
.Replace(Char.ConvertFromUtf32(&HFFFB), "\uFFFB"))
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| | System.Collections.Generic.List<string> srcs =
new System.Collections.Generic.List<string>()
{ "笑", "う", "門", "には", "福", "来", "る。" };
System.Collections.Generic.List<string> anns =
new System.Collections.Generic.List<string>()
{ "わら", "", "かど", "", "ふく", "きた", "" };
Microsoft.International.Collections.StringAnnotations strAnns =
new Microsoft.International.Collections.StringAnnotations(srcs, anns);
Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider fp =
new Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider();
string result = strAnns.ToString(null, fp);
Console.WriteLine(result.Replace("\uFFF9", "\\uFFF9")
.Replace("\uFFFA", "\\uFFFA")
.Replace("\uFFFB", "\\uFFFB"));
|
この結果、以下のような文字列が出力されます。
\uFFF9笑\uFFFAわら\uFFFBう\uFFF9門\uFFFAかど\uFFFBには\uFFF9福\uFFFAふく\uFFFB\uFFF9来\uFFFAきた\uFFFBる。 |
前のコードと違うのは、StringAnnotations.ToStringメソッドの2番目のパラメータにStringAnnotationInterLinearAnnotationFormatProviderオブジェクトを指定している点です。ToStringメソッドの2番目のパラメータに適当なStringAnnotationsFormatProviderオブジェクトを指定することで、ルビの振り方を指定できます。StringAnnotationInterLinearAnnotationFormatProviderクラスはStringAnnotationsFormatProviderクラスから派生したクラスで、ユニコード上のルビを振るための書式情報を提供します。
コード内のコメントにも書きましたが、Yomigana Frameworkのヘルプの「StringAnnotations クラスの注釈付き文字列を Unicode の Interlinear Annotation 形式で書式指定する方法」で紹介されている方法は、StringAnnotationInterLinearAnnotationFormatProviderのFormatメソッドを呼び出す方法です。私個人としてはToStringを使った方が簡単で分かりやすいと思いましたので、こちらを使いました。今後もToStringを使った方法のみを紹介しますが、Formatを使ってもできます。
JIS X 4052上のルビを振る †
同じように、JIS X 4052上のルビを振るには、StringAnnotationsFormatProviderから派生したStringAnnotationJisX4052FormatProviderクラスを使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| | Dim srcs As New System.Collections.Generic.List(Of String)() From { _
"笑", "う", "門", "には","福", "来", "る。" _
}
Dim anns As New System.Collections.Generic.List(Of String)() From { _
"わら", "", "かど", "", "ふく", "きた", "" _
}
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
srcs, anns)
Dim fp As New Microsoft.International.StringAnnotationJisX4052FormatProvider()
Dim result As String = strAnns.ToString(Nothing, fp)
Console.WriteLine(result)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| | System.Collections.Generic.List<string> srcs =
new System.Collections.Generic.List<string>()
{ "笑", "う", "門", "には", "福", "来", "る。" };
System.Collections.Generic.List<string> anns =
new System.Collections.Generic.List<string>()
{ "わら", "", "かど", "", "ふく", "きた", "" };
Microsoft.International.Collections.StringAnnotations strAnns =
new Microsoft.International.Collections.StringAnnotations(srcs, anns);
Microsoft.International.StringAnnotationJisX4052FormatProvider fp =
new Microsoft.International.StringAnnotationJisX4052FormatProvider();
string result = strAnns.ToString(null, fp);
Console.WriteLine(result);
|
この結果、以下のような文字列が出力されます。
_^笑(わら)^_う_^門(かど)^_には_^福(ふく)^__^来(きた)^_る。 |
読み仮名を指定した文字列で囲む †
一番初めの例ではルビが丸括弧で囲まれましたが、StringAnnotationInterOpenCloseBracketsFormatProviderを使えば、ルビを指定した文字列で囲むことができます。
以下の例では、<< >> で囲んでいます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | Dim srcs As New System.Collections.Generic.List(Of String)() From { _
"笑", "う", "門", "には","福", "来", "る。" _
}
Dim anns As New System.Collections.Generic.List(Of String)() From { _
"わら", "", "かど", "", "ふく", "きた", "" _
}
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
srcs, anns)
Dim fp As New _
Microsoft.International.StringAnnotationInterOpenCloseBracketsFormatProvider()
fp.OpenBracket = "<<"
fp.CloseBracket = ">>"
Dim result As String = strAnns.ToString(Nothing, fp)
Console.WriteLine(result)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | System.Collections.Generic.List<string> srcs =
new System.Collections.Generic.List<string>()
{ "笑", "う", "門", "には", "福", "来", "る。" };
System.Collections.Generic.List<string> anns =
new System.Collections.Generic.List<string>()
{ "わら", "", "かど", "", "ふく", "きた", "" };
Microsoft.International.Collections.StringAnnotations strAnns =
new Microsoft.International.Collections.StringAnnotations(srcs, anns);
Microsoft.International.StringAnnotationInterOpenCloseBracketsFormatProvider fp =
new Microsoft.International.StringAnnotationInterOpenCloseBracketsFormatProvider();
fp.OpenBracket = "<<";
fp.CloseBracket = ">>";
string result = strAnns.ToString(null, fp);
Console.WriteLine(result);
|
HTML上のルビを振る †
HTML上のルビを振るためのStringAnnotationsFormatProviderは、Yomigana Frameworkには用意されていません。それでは自分で作ればよいかというと、それも難しいです。というのは、先に指摘したように、StringAnnotationsクラスには指定したインデックスの親文字を取得できる方法が用意されていないからです。
そこで、ユニコード上のルビをHTML上のルビに変換するメソッドを作ってみました。単純にReplaceメソッドで文字列置換しているだけですので、ユニコードのルビ表示制御文字が挿入されている位置が想定されている位置と違った場合は、HTMLが正しくなくなる可能性があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| | Public Shared Function ReplaceInterlinearCharToHtmlTag(ByVal interLinear As String, _
ByVal rpOpen As String, _
ByVal rpClose As String) _
As String
If interLinear Is Nothing Then
Return interLinear
End If
Dim sb As New System.Text.StringBuilder(interLinear)
sb.Replace(Char.ConvertFromUtf32(&HFFF9), _
"<ruby><rb>")
If String.IsNullOrEmpty(rpOpen) Then
sb.Replace(Char.ConvertFromUtf32(&HFFFA), _
"</rb><rt>")
Else
sb.Replace(Char.ConvertFromUtf32(&HFFFA), _
"</rb><rp>" & rpOpen & "</rp><rt>")
End If
If String.IsNullOrEmpty(rpClose) Then
sb.Replace(Char.ConvertFromUtf32(&HFFFB), _
"</rt></ruby>")
Else
sb.Replace(Char.ConvertFromUtf32(&HFFFB), _
"</rt><rp>" & rpClose & "</rp></ruby>")
End If
Return sb.ToString()
End Function
Public Shared Function ReplaceInterlinearCharToHtmlTag(ByVal interLinear As String) _
As String
Return ReplaceInterlinearCharToHtmlTag(interLinear, "(", ")")
End Function
End Class
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
| | public static string ReplaceInterlinearCharToHtmlTag(
string interLinear, string rpOpen, string rpClose)
{
if (interLinear == null)
{
return interLinear;
}
System.Text.StringBuilder sb = new System.Text.StringBuilder(interLinear);
sb.Replace("\ufff9", "<ruby><rb>");
if (string.IsNullOrEmpty(rpOpen))
{
sb.Replace("\ufffa", "</rb><rt>");
}
else
{
sb.Replace("\ufffa", "</rb><rp>" + rpOpen + "</rp><rt>");
}
if (string.IsNullOrEmpty(rpClose))
{
sb.Replace("\ufffb", "</rt></ruby>");
}
else
{
sb.Replace("\ufffb", "</rt><rp>" + rpClose + "</rp></ruby>");
}
return sb.ToString();
}
public static string ReplaceInterlinearCharToHtmlTag(string interLinear)
{
return ReplaceInterlinearCharToHtmlTag(interLinear, "(", ")");
}
|
使い方は、以下のような感じです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | Dim srcs As New System.Collections.Generic.List(Of String)() From { _
"笑", "う", "門", "には","福", "来", "る。" _
}
Dim anns As New System.Collections.Generic.List(Of String)() From { _
"わら", "", "かど", "", "ふく", "きた", "" _
}
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
srcs, anns)
Dim fp As New _
Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider()
Dim result As String = strAnns.ToString(Nothing, fp)
Dim html As String = ReplaceInterlinearCharToHtmlTag(result)
Console.WriteLine(html)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | System.Collections.Generic.List<string> srcs =
new System.Collections.Generic.List<string>()
{ "笑", "う", "門", "には", "福", "来", "る。" };
System.Collections.Generic.List<string> anns =
new System.Collections.Generic.List<string>()
{ "わら", "", "かど", "", "ふく", "きた", "" };
Microsoft.International.Collections.StringAnnotations strAnns =
new Microsoft.International.Collections.StringAnnotations(srcs, anns);
Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider fp =
new Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider();
string result = strAnns.ToString(null, fp);
string html = ReplaceInterlinearCharToHtmlTag(result);
Console.WriteLine(html);
|
この結果、以下のように出力されます。
<ruby><rb>笑</rb><rp>(</rp><rt>わら</rt><rp>)</rp></ruby>う<ruby><rb>門</rb><rp>(</rp><rt>かど</rt><rp>)</rp></ruby>には<ruby><rb>福</rb><rp>(</rp><rt>ふく</rt><rp>)</rp></ruby><ruby><rb>来</rb><rp>(</rp><rt>きた</rt><rp>)</rp></ruby>る。 |
なおユニコード上のルビからHTML上のルビへの変換は、ASP.NETサーバーコントロールのSimpleRubyを使っても出来ます。SimpleRubyについては、後で紹介します。
ルビが振られた文字列からStringAnnotationsオブジェクトを作成する †
ルビが振られた文字列からStringAnnotationsオブジェクトを作成するには、StringAnnotations.Parseメソッドを使います。
ユニコード上のルビが振られた文字列からStringAnnotationsオブジェクトを作成する †
ユニコード上のルビが振られた文字列から作成する場合は、その文字列をParseメソッドのパラメータに指定するだけでStringAnnotationsオブジェクトが返されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| | Dim source As String = Char.ConvertFromUtf32(&HFFF9) & "笑" & _
Char.ConvertFromUtf32(&HFFFA) & "わら" & Char.ConvertFromUtf32(&HFFFB) & _
"う" & Char.ConvertFromUtf32(&HFFF9) & "門" & Char.ConvertFromUtf32(&HFFFA) & _
"かど" & Char.ConvertFromUtf32(&HFFFB) & "には" & "" & _
Char.ConvertFromUtf32(&HFFF9) & "福" & Char.ConvertFromUtf32(&HFFFA) & "ふく" & _
Char.ConvertFromUtf32(&HFFFB) & "" & Char.ConvertFromUtf32(&HFFF9) & "来" & _
Char.ConvertFromUtf32(&HFFFA) & "きた" & Char.ConvertFromUtf32(&HFFFB) & "る。"
Dim strAnns As Microsoft.International.Collections.StringAnnotations = _
Microsoft.International.Collections.StringAnnotations.Parse(source)
Dim result As String = strAnns.ToString(Nothing, Nothing)
Console.WriteLine(result)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| | string source = "\uFFF9笑\uFFFAわら\uFFFBう\uFFF9門\uFFFAかど\uFFFBには" +
"\uFFF9福\uFFFAふく\uFFFB\uFFF9来\uFFFAきた\uFFFBる。";
Microsoft.International.Collections.StringAnnotations strAnns =
Microsoft.International.Collections.StringAnnotations.Parse(source);
string result = strAnns.ToString(null, null);
Console.WriteLine(result);
|
JIS X 4052上のルビが振られた文字列からStringAnnotationsオブジェクトを作成する †
JIS X 4052上のルビが振られた文字列からStringAnnotationsオブジェクトを作成するには、ParseメソッドにStringAnnotationJisX4052FormatProviderオブジェクトを指定します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| | Dim source As String = "_^笑(わら)^_う_^門(かど)^_には" & _
"_^福(ふく)^__^来(きた)^_る。"
Dim strAnns As Microsoft.International.Collections.StringAnnotations = _
Microsoft.International.Collections.StringAnnotations.Parse( _
source, New Microsoft.International.StringAnnotationJisX4052FormatProvider())
Dim result As String = strAnns.ToString(Nothing, Nothing)
Console.WriteLine(result)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| | string source = "_^笑(わら)^_う_^門(かど)^_には" +
"_^福(ふく)^__^来(きた)^_る。";
Microsoft.International.Collections.StringAnnotations strAnns =
Microsoft.International.Collections.StringAnnotations.Parse(source,
new Microsoft.International.StringAnnotationJisX4052FormatProvider());
string result = strAnns.ToString(null, null);
Console.WriteLine(result);
|
ルビが括弧で囲まれた文字列からStringAnnotationsオブジェクトを作成する †
ルビが括弧で囲まれた文字列からStringAnnotationsオブジェクトを作成するには、上の方法と同じようにParseメソッドの2番目のパラメータにStringAnnotationInterOpenCloseBracketsFormatProviderオブジェクトを指定する方法もありますが、それ以外に、Parseメソッドの2番目のパラメータに文字列の配列を指定する方法もあります。この場合配列とする文字列には、"()", "<>", "[]"のようにルビを囲んでいる括弧の種類を2文字の文字列で指定します。2文字の文字列しか受け付けないようなので、開き括弧と閉じ括弧が1文字の括弧しか指定できません。
また、ルビが括弧で囲まれている形式では親文字が明確でありませんので、そのままでは間違って解釈されてしまいます。例えば「えび天(てん)」は「天」だけに「てん」というルビが振られていますが、このままでは「えび天」全体に「てん」というルビが振られていると解釈されてしまいます。これを防ぐには、「えび()天(てん)」という風に親文字の前に空の括弧を挿入します。
この方法によってStringAnnotationsオブジェクトを作成する例を以下に示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| | Dim source As String = "笑(わら)う()門<かど>には()福[ふく]来{きた}る。"
Dim strAnns As Microsoft.International.Collections.StringAnnotations = _
Microsoft.International.Collections.StringAnnotations.Parse( _
source, New String() {"()", "<>", "[]", "{}"})
Dim result As String = strAnns.ToString( _
Nothing, New Microsoft.International.StringAnnotationJisX4052FormatProvider())
Console.WriteLine(result)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| | string source = "笑(わら)う()門<かど>には()福[ふく]来{きた}る。";
Microsoft.International.Collections.StringAnnotations strAnns =
Microsoft.International.Collections.StringAnnotations.Parse(source,
new string[] { "()", "<>", "[]", "{}" });
string result = strAnns.ToString(null,
new Microsoft.International.StringAnnotationJisX4052FormatProvider());
Console.WriteLine(result);
|
ルビが振られた文字列を比較する †
ユニコード上のルビが振られた2つの文字列が等しいか調べる †
Yomigana Frameworkには、ユニコード上のルビが振られた文字列を比較する方法が用意されています。それには、InterlinearAnnotationComparerクラスのCompareメソッドを使います。比較した両者が等しいときは、Compareメソッドが0を返します。
等しいと判断されるのは、すべてのルビと、その親文字が等しい時のようです。つまり、ルビが振られていない箇所は違っていても関係ないようです。
また、ルビの数が違っていても、それ以外が同じならば、同じと判断されます。例えば、"\uFFF9福\uFFFAふく\uFFFB\uFFF9来\uFFFAきた\uFFFBる。"と"\uFFF9福\uFFFAふく\uFFFB"は同じと判断されます。さらに言えば、ルビが振られた文字列と、ルビが全く振られていない文字列を比較しても等しいと判断されます。これはどう考えてもおかしいので、バグだと思われます。
ルビとその親文字をどのような方法で比較するか(大文字小文字を区別するかなど)は、InterlinearAnnotationComparerのコンストラクタにStringComparerを使って指定します。StringComparerについては、「文字列の配列やコレクションを並び替える」をご覧ください。
なお、ルビだけ、あるいは親文字だけを比較する方法は用意されていないようです。
以下の例では、序数の文字列比較を使って比較しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| | Dim s1 As String = Char.ConvertFromUtf32(&HFFF9) & "福" & _
Char.ConvertFromUtf32(&HFFFA) & "ふく" & Char.ConvertFromUtf32(&HFFFB) & _
"" & Char.ConvertFromUtf32(&HFFF9) & "来" & Char.ConvertFromUtf32(&HFFFA) & _
"きた" & Char.ConvertFromUtf32(&HFFFB) & "る。"
Dim s2 As String = Char.ConvertFromUtf32(&HFFF9) & "福" & _
Char.ConvertFromUtf32(&HFFFA) & "ふく" & Char.ConvertFromUtf32(&HFFFB) & _
"" & Char.ConvertFromUtf32(&HFFF9) & "来" & Char.ConvertFromUtf32(&HFFFA) & _
"きた" & Char.ConvertFromUtf32(&HFFFB) & "よ。"
Dim comparer As New Microsoft.International.InterlinearAnnotationComparer( _
StringComparer.Ordinal)
Dim result As Integer = comparer.Compare(s1, s2)
Console.WriteLine(result)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| | string s1 = "\uFFF9福\uFFFAふく\uFFFB\uFFF9来\uFFFAきた\uFFFBる。";
string s2 = "\uFFF9福\uFFFAふく\uFFFB\uFFF9来\uFFFAきた\uFFFBよ。";
Microsoft.International.InterlinearAnnotationComparer comparer =
new Microsoft.International.InterlinearAnnotationComparer(
StringComparer.Ordinal);
int result = comparer.Compare(s1, s2);
Console.WriteLine(result);
|
なおInterlinearAnnotationComparerの代わりにStringAnnotationComparerを使うことによって、StringAnnotationsオブジェクトを比較することもできます。
ひらがなとカタカナ、小書きの仮名と普通の仮名、濁音や半濁音と清音を区別しないで比較する †
ひらがなとカタカナを区別しないで比較したり、小書きの仮名と普通の仮名(「ぁ」と「あ」など)を区別しないで比較したり、濁音や半濁音と清音(「ば」と「は」、「ぱ」と「は」など)を区別しないで比較することもできます。
以下にその例を示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| | Dim s1 As String = Char.ConvertFromUtf32(&HFFF9) & "福" & _
Char.ConvertFromUtf32(&HFFFA) & "ふく" & Char.ConvertFromUtf32(&HFFFB) & _
"" & Char.ConvertFromUtf32(&HFFF9) & "来" & Char.ConvertFromUtf32(&HFFFA) & _
"きた" & Char.ConvertFromUtf32(&HFFFB) & "る。"
Dim s2 As String = Char.ConvertFromUtf32(&HFFF9) & "福" & _
Char.ConvertFromUtf32(&HFFFA) & "フク" & Char.ConvertFromUtf32(&HFFFB) & _
"" & Char.ConvertFromUtf32(&HFFF9) & "来" & Char.ConvertFromUtf32(&HFFFA) & _
"ギダ" & Char.ConvertFromUtf32(&HFFFB) & "よ。"
Dim comp1 As New Microsoft.International.InterlinearAnnotationComparer( _
StringComparer.Ordinal)
Dim comp2 As New Microsoft.International.IgnoreKanaKindComparison(comp1)
Dim comp3 As New Microsoft.International.IgnoreSmallKanaLetterComparison(comp2)
Dim comp4 As New _
Microsoft.International.IgnoreVoicedSemiVoicedSoundMarksComparison(comp3)
Dim result As Integer = comp4.Compare(s1, s2)
Console.WriteLine(result)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| | string s1 = "\uFFF9福\uFFFAふく\uFFFB\uFFF9来\uFFFAきた\uFFFBる。";
string s2 = "\uFFF9福\uFFFAフク\uFFFB\uFFF9来\uFFFAギダ\uFFFBよ。";
Microsoft.International.InterlinearAnnotationComparer comp1 =
new Microsoft.International.InterlinearAnnotationComparer(StringComparer.Ordinal);
Microsoft.International.IgnoreKanaKindComparison comp2 =
new Microsoft.International.IgnoreKanaKindComparison(comp1);
Microsoft.International.IgnoreSmallKanaLetterComparison comp3 =
new Microsoft.International.IgnoreSmallKanaLetterComparison(comp2);
Microsoft.International.IgnoreVoicedSemiVoicedSoundMarksComparison comp4 =
new Microsoft.International.IgnoreVoicedSemiVoicedSoundMarksComparison(comp3);
int result = comp4.Compare(s1, s2);
Console.WriteLine(result);
|
大小の比較について †
このようなCompareメソッドによる比較では、2つが等しいかだけでなく、どちらが大きいかを調べることもできます。どのように大小を比較しているのかは、次のように行っているようです。
- インデックスが0のルビをStringComparer.Compareメソッドで比較し、0でなければ、その結果を返す。0ならば、次へ。
- インデックスが0の親文字をStringComparer.Compareメソッドで比較し、0でなければ、その結果を返す。0ならば、次へ。
- 節がもうなければ、0を返す。まだあれば、次のインデックスに移って、1,2を繰り返す。
つまり、ルビの大小がまず比較され、次に親文字が比較されます。
次回予告 †
今回はStringAnnotationsクラスについて色々調べてみましたが、次回はYomigana Frameworkに含まれているコントロールのYomiganaTextBoxなどをいじってみる予定です。
コメント †
|