「青空文庫」をWordVBAで攻略する(1)
「青空文庫」のテキストからルビ用文字列を除去する
「青空文庫」からダウンロードしたテキストファイルを、WordVBAを用いて整形していきます。
今回は、手始めに〝ルビ用文字列〟の除去を行います。
目次
こんなことができます
「青空文庫」からテキストデータをダウンロードすると、
中身はこうなっている。
見ての通り、
櫂《かい》の木太刀
本文中でルビが振られている文字の後ろに、「《》
」で括ってルビの文字列を示してある。
これはこれで実に重要な情報なのだが、読む際にはただただ邪魔である。
このテキストデータをWordに貼り付けた後、読みやすいように「《》
」で括られた部分を除去したい。
これを、
こういう状態にするのである。
ルビ用文字列の位置を取得する
考え方
除去するためには、まず場所を特定せねばならん。
これは簡単。ルビ文字列はことごとく「《》
」で括られているのだから、「《
」の位置と「》
」の位置を取得して、その範囲を取得すればよい。
文字を検索し、その位置を返すFunction
Find
オブジェクトを利用するFunctionを作る。
リスト1 標準モジュールFormatStrings
Private Function getNextPosition( _ ByVal FindText As String) As Long '……(1)' Dim ret As Long '……(2)' ret = -1 With Selection.Find '……(3)' .Text = FindText .Replacement.Text = "" .Wrap = wdFindStop .Format = False .Highlight = False .MatchCase = False .MatchWholeWord = False .MatchByte = False .MatchAllWordForms = False .MatchSoundsLike = False .MatchWildcards = False .MatchFuzzy = False End With Call Selection.Find.Execute '……(4)' If Not Selection.Find.Found Then GoTo ReturnValue '……(5)' ret = Selection.Range.Start '……(6)' Call Selection.Collapse(wdCollapseEnd) '……(7)' ReturnValue: '……(8)' getNextPosition = ret End Function
まず、(1)の
Private Function getNextPosition( _ ByVal FindText As String) As Long
で引数と返り値を設定。
検索文字列を受け取って、ドキュメント内での位置を表す整数を返すようにした。
(2)からの2行
Dim ret As Long ret = -1
返り値用の変数ret
を用意して、初期値-1
を設定。
文字列の位置が負の数になることはないので、検索がうまくいかなかったことがわかるようにこうしている。
(3)からの14行
With Selection.Find .Text = FindText .Replacement.Text = "" .Wrap = wdFindStop .Format = False .Highlight = False .MatchCase = False .MatchWholeWord = False .MatchByte = False .MatchAllWordForms = False .MatchSoundsLike = False .MatchWildcards = False .MatchFuzzy = False End With
は、おなじみ、Find
オブジェクトの初期設定。
純粋に文字列を検索したいだけなので、ほとんどの項目がFalse
。
Wrap
プロパティをwdFindStop
にしているのは、後方検索オンリーにしたいがゆえ。
この辺りの挙動は、となりITさんのこちらの記事が詳しいのでどうぞ。
(4)の
Call Selection.Find.Execute
で検索実行。
(5)の
If Not Selection.Find.Found Then GoTo ReturnValue
でFound
プロパティを調べる。
Found
プロパティがFalse
だということは、そもそも検索でヒットしなかったということなので、即座にReturnValue
ラベルに吹っ飛ばして、-1
を返す。
(6)にたどり着いたということは、検索でヒットしたということ。
この時点で、Wordドキュメント上ではヒットした文字列が選択された状態になっている。
たとえば、「ち~んw」という文字列がヒットした場合、(5)の実行後は、
こんな状態になっている。
そこで、(6)の
ret = Selection.Range.Start
で選択箇所の始端位置を取得し、返り値用変数ret
にぶち込む。
このままだと、検索でヒットした箇所が選択状態なので、次に検索するときに検索対象がこの箇所オンリーになってしまって困る。
そこで、(7)の
Call Selection.Collapse(wdCollapseEnd)
で、選択箇所を後方に潰しておく。
あとは、(8)の
ReturnValue: getNextPosition = ret
で返り値をセットしておしまい。
ルビ文字列の箇所を表すRangeオブジェクト
これで、ルビ文字列の箇所、すなわち〝「《》
」で括られた文字列の箇所〟を取得する準備がととのった。
ここからの手順は次の通り。すなわち、
- リスト1の
getNextPosition
メソッドを用いて、「《
」の位置を取得する - 同様に「
》
」の位置を取得する [Document].Range
メソッドに上記1.・2.で取得した開始位置・終了位置を渡してRange
オブジェクトを取得する
これでオッケーである。
コードとしては、
Dim startPos As Long startPos = getNextPosition("《") Dim endPos As Long endPos = getNextPosition("》") + 1 '……(*)' Dim tgtRange As Range Set tgtRange = ActiveDocument.Range(startPos, endPos)
とすればオッケー。
(*)のところで「 + 1
」しているのは、getNextPosition
メソッドが検索でヒットした文字列の開始位置を取得するから。
ヒットした文字列(この場合は「》
」)の文字数分だけプラスしてやれば終了位置になる。
ルビ用文字列を除去する
ここまでで、除去対象の箇所を表すRange
が取得できている。
あとは簡単。
[Range].Text
プロパティに""
をセットしてやればよい。上のコードで言えば、
tgtRange.Text = ""
とすればよい。楽勝。
ルビ用文字列を次々に除去する
「《》」で括られた箇所を除去するFunction
まず、先に記した手順をひとまとめにしたFunctionを作っておく。
リスト2 標準モジュールFormatStrings
Public Function ReplaceSpecifiedRange( _ ByVal StartChar As String, _ ByVal EndChar As String, _ Optional ByVal ReplaceText As String = "") As Boolean ReplaceSpecifiedRange = False 'getNextPositionは、文字列がみつからなかったら-1を返す' Const NOT_FOUND As Long = -1 Dim startPos As Long startPos = getNextPosition(StartChar) If startPos = NOT_FOUND Then Exit Function Dim endPos As Long endPos = getNextPosition(EndChar) If endPos = NOT_FOUND Then Exit Function endPos = endPos + Len(EndChar) If startPos > endPos Then Exit Function Dim tgtDoc As Document Set tgtDoc = ActiveDocument Dim tgtRange As Range Set tgtRange = tgtDoc.Range(startPos, endPos) tgtRange.Text = ReplaceText ReplaceSpecifiedRange = True End Function
引数StartChar
とEndChar
で挟まれた文字列を、まるごと引数ReplaceText
の文字列に置き換える、というもの。
今回の例であれば、
Call FormatStrings.ReplaceSpecifiedRange("《", "》", "")
とすれば、たとえば「《ち~んw》
」をごっそり除去できる、ということになる。
上ではCall
を用いたが、置換に成功すればTrue
、置換できなければFalse
を返すようにしているので、たとえば、
Do If Not FormatStrings.ReplaceSpecifiedRange("《", "》", "") Then Exit Do End If Loop
とでもしておけば、文書の終端まで除去し続けることになる。
こんな感じに。(わかりやすいように、Sleep
をかましています。)
おわりに
手作業で除去しようとすると恐ろしい手間がかかりますが、これだと楽勝。
こういう場面では、WordのVBAが大活躍します。
補足
ワイルドカードを用いて〝「《 》
」で括られた箇所を取得する〟方法については、
コチラをどうぞ。