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 { _
    "わら", "", "かど", "", "ふく", "きた", "" _
}
 
'StringAnnotationsオブジェクトを作成する
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>()
    { "わら", "", "かど", "", "ふく", "きた", "" };
 
//StringAnnotationsオブジェクトを作成する
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 { _
    "わら", "", "かど", "", "ふく", "きた", "" _
}
 
'StringAnnotationsオブジェクトを作成する
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>()
    { "わら", "", "かど", "", "ふく", "きた", "" };
 
//StringAnnotationsオブジェクトを作成する
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 { _
    "わら", "", "かど", "", "ふく", "きた", "" _
}
 
'StringAnnotationsオブジェクトを作成する
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
    srcs, anns)
'StringAnnotationInterLinearAnnotationFormatProviderオブジェクトを作成する
Dim fp As New  _
    Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider()
'ユニコード上のルビが振られた文章を取得する
Dim result As String = strAnns.ToString(Nothing, fp)
 
'ヘルプの「Yomigana Framework Library の How Do I」では次のような方法が紹介されている
'Dim result As String = fp.Format(fp.FormatString, strAnns, 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>()
    { "わら", "", "かど", "", "ふく", "きた", "" };
 
//StringAnnotationsオブジェクトを作成する
Microsoft.International.Collections.StringAnnotations strAnns =
    new Microsoft.International.Collections.StringAnnotations(srcs, anns);
//StringAnnotationInterLinearAnnotationFormatProviderオブジェクトを作成する
Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider fp =
    new Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider();
//ユニコード上のルビが振られた文章を取得する
string result = strAnns.ToString(null, fp);
 
//ヘルプの「Yomigana Framework Library の How Do I」では次のような方法が紹介されている
//string result = fp.Format(fp.FormatString, strAnns, 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 { _
    "わら", "", "かど", "", "ふく", "きた", "" _
}
 
'StringAnnotationsオブジェクトを作成する
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
    srcs, anns)
'StringAnnotationJisX4052FormatProviderオブジェクトを作成する
Dim fp As New Microsoft.International.StringAnnotationJisX4052FormatProvider()
'JIS X 4052上のルビが振られた文章を取得する
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>()
    { "わら", "", "かど", "", "ふく", "きた", "" };
 
//StringAnnotationsオブジェクトを作成する
Microsoft.International.Collections.StringAnnotations strAnns =
    new Microsoft.International.Collections.StringAnnotations(srcs, anns);
//StringAnnotationJisX4052FormatProviderオブジェクトを作成する
Microsoft.International.StringAnnotationJisX4052FormatProvider fp =
    new Microsoft.International.StringAnnotationJisX4052FormatProvider();
//JIS X 4052上のルビが振られた文章を取得する
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 { _
    "わら", "", "かど", "", "ふく", "きた", "" _
}
 
'StringAnnotationsオブジェクトを作成する
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
    srcs, anns)
'StringAnnotationInterOpenCloseBracketsFormatProviderオブジェクトを作成する
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>()
    { "わら", "", "かど", "", "ふく", "きた", "" };
 
//StringAnnotationsオブジェクトを作成する
Microsoft.International.Collections.StringAnnotations strAnns =
    new Microsoft.International.Collections.StringAnnotations(srcs, anns);
//StringAnnotationInterOpenCloseBracketsFormatProviderオブジェクトを作成する
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
''' <summary>
''' 指定された文字列内のUnicodeルビ表示制御文字をHTMLタグに置換する
''' </summary>
''' <param name="interLinear">
''' Unicodeルビ表示制御文字を含む文字列
''' </param>
''' <param name="rpOpen">
''' RUBYタグに対応していないブラウザでルビの前に表示する文字列。
''' </param>
''' <param name="rpClose">
''' RUBYタグに対応していないブラウザでルビの後ろに表示する文字列。
''' </param>
''' <returns>
''' interLinearのUnicodeルビ表示制御文字をHTMLタグに置換した文字列。
''' </returns>
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
 
''' <summary>
''' 指定された文字列内のUnicodeルビ表示制御文字をHTMLタグに置換する
''' </summary>
''' <param name="interLinear">
''' Unicodeルビ表示制御文字を含む文字列
''' </param>
''' <returns>
''' interLinearのUnicodeルビ表示制御文字をHTMLタグに置換した文字列。
''' </returns>
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
/// <summary>
/// 指定された文字列内のUnicodeルビ表示制御文字をHTMLタグに置換する
/// </summary>
/// <param name="interLinear">
/// Unicodeルビ表示制御文字を含む文字列
/// </param>
/// <param name="rpOpen">
/// RUBYタグに対応していないブラウザでルビの前に表示する文字列。
/// </param>
/// <param name="rpClose">
/// RUBYタグに対応していないブラウザでルビの後ろに表示する文字列。
/// </param>
/// <returns>
/// interLinearのUnicodeルビ表示制御文字をHTMLタグに置換した文字列。
/// </returns>
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();
}
 
/// <summary>
/// 指定された文字列内のUnicodeルビ表示制御文字をHTMLタグに置換する
/// </summary>
/// <param name="interLinear">
/// Unicodeルビ表示制御文字を含む文字列
/// </param>
/// <returns>
/// interLinearのUnicodeルビ表示制御文字をHTMLタグに置換した文字列。
/// </returns>
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 { _
    "わら", "", "かど", "", "ふく", "きた", "" _
}
 
'StringAnnotationsオブジェクトを作成する
Dim strAnns As New Microsoft.International.Collections.StringAnnotations( _
    srcs, anns)
'StringAnnotationInterLinearAnnotationFormatProviderオブジェクトを作成する
Dim fp As New  _
    Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider()
'ユニコード上のルビが振られた文章を取得する
Dim result As String = strAnns.ToString(Nothing, fp)
 
'HTML上のルビに変換する
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>()
    { "わら", "", "かど", "", "ふく", "きた", "" };
 
//StringAnnotationsオブジェクトを作成する
Microsoft.International.Collections.StringAnnotations strAnns =
    new Microsoft.International.Collections.StringAnnotations(srcs, anns);
//StringAnnotationInterLinearAnnotationFormatProviderオブジェクトを作成する
Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider fp =
    new Microsoft.International.StringAnnotationInterLinearAnnotationFormatProvider();
//ユニコード上のルビが振られた文章を取得する
string result = strAnns.ToString(null, fp);
 
//HTML上のルビに変換する
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) & "る。"
 
'StringAnnotationsオブジェクトを作成する
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る。";
 
//StringAnnotationsオブジェクトを作成する
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
'JIS X 4052上のルビが振られた文字列
Dim source As String = "_^笑(わら)^_う_^門(かど)^_には" & _
    "_^福(ふく)^__^来(きた)^_る。"
 
'StringAnnotationsオブジェクトを作成する
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
//JIS X 4052上のルビが振られた文字列
string source = "_^笑(わら)^_う_^門(かど)^_には" +
    "_^福(ふく)^__^来(きた)^_る。";
 
//StringAnnotationsオブジェクトを作成する
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 = "笑(わら)う()門<かど>には()福[ふく]来{きた}る。"
 
'ルビを囲んでいる括弧の種類を指定して、StringAnnotationsオブジェクトを作成する
Dim strAnns As Microsoft.International.Collections.StringAnnotations = _
    Microsoft.International.Collections.StringAnnotations.Parse( _
        source, New String() {"()", "<>", "[]", "{}"})
 
'JIS X 4052上のルビが振られた文章を取得する
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 = "笑(わら)う()門<かど>には()福[ふく]来{きた}る。";
 
//ルビを囲んでいる括弧の種類を指定して、StringAnnotationsオブジェクトを作成する
Microsoft.International.Collections.StringAnnotations strAnns =
    Microsoft.International.Collections.StringAnnotations.Parse(source,
    new string[] { "()", "<>", "[]", "{}" });
 
//JIS X 4052上のルビが振られた文章を取得する
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)
'0
  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);
//0

なお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)
'0
  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);
//0

大小の比較について

このようなCompareメソッドによる比較では、2つが等しいかだけでなく、どちらが大きいかを調べることもできます。どのように大小を比較しているのかは、次のように行っているようです。

  1. インデックスが0のルビをStringComparer.Compareメソッドで比較し、0でなければ、その結果を返す。0ならば、次へ。
  2. インデックスが0の親文字をStringComparer.Compareメソッドで比較し、0でなければ、その結果を返す。0ならば、次へ。
  3. 節がもうなければ、0を返す。まだあれば、次のインデックスに移って、1,2を繰り返す。

つまり、ルビの大小がまず比較され、次に親文字が比較されます。

次回予告

今回はStringAnnotationsクラスについて色々調べてみましたが、次回はYomigana Frameworkに含まれているコントロールのYomiganaTextBoxなどをいじってみる予定です。

コメント



ページ情報
[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]