見た目が同一の文字だけど、文字コードが異なっているという意味で「文字化けの問題」を解決する
見た目が同一の文字だけど、文字コードが異なっているという意味で「文字化けの問題」を解決する
見た目が同一の文字だけど、文字コードが異なっているという意味で「文字化けの問題」を解決する記事です。康煕部首文字関係のVBA Macroを作成する技術記事です。
本記事を基に作成したWordのmacroは次のページにあります。
1.問題点
問題発生の流れ:メイリオフォントで文書作成>PDF化>Wordなどのエディタで読む>任意の文字を検索してもヒットしないので、あれ?と感じる
1.問題点
文書中の文字列を検索できるアプリ(例えばWordやAcrobat Readerなどの「検索」コマンドを実装するアプリ)において、任意の文字が文書中にあるにも関わらず、その文字を「検索」してもヒットしない場合がある。例えば、文書中に「高齢者」という文字があることが視認できるにも関わらず、「高齢者」で検索をしても、文書中の「高齢者」がヒットせず、「文書の検索が終了しました。一致するものはありませんでした」というエラーになる場合がある。
これは、文書中の「高齢者」の「高」の文字が康煕部首文字であることを疑ってみても良い。
下図は、Wordで「東大阪市長」と入力されている。「長」の文字を検索しても「検索条件に一致するものは見つかりませんでした。」とエラーになる。当然「市長」や「東大阪市長」で検索しても「一致なし」になる。
Word文書で行間の広い行が発生(コンピュータ)にも別の問題を記載した。
これらの問題が康煕部首文字などに起因することは分かっていた。
(参考)文字列が康煕部首に自動変換される件(コンピュータ)
(参考)文字列がCJK Radicals Supplementに自動変換される件
「メモ帳」で目視&手作業修正を行えば解決するが、それは大変なので、マクロ化できないか検討した。
2.問題のある文書ファイル
問題のある文書ファイルは次のPDFファイルです。
http://www.city.higashiosaka.lg.jp/cmsfiles/contents/0000028/28226/honpen.pdf
本記事では、このPDFファイルの文字列データをWordへ貼り付けて作業をしています。
東大阪市第3次総合計画は、東大阪市役所のホームページからダウンロードできますが、修正や削除されるかもしれないため、下記URLからもダウンロードできるようにしました。
上記URLから次の2個のファイルをダウンロードできます。
・東大阪市第3次総合計画 本編
・東大阪市第3次総合計画 概要
なお、WARPからでもダウンロードできます。
(参考)東大阪市第3次総合計画 人権尊重に目標値50%を設定 (社会的観点からの記事です)....人権尊重に目標値を定めるって変だと思いませんか?
3.変換テーブルの検討
康煕部首文字をMicrosoft Visual Basic for Applicationsに組み込もうとしたが、プログラミングエディタ上で、この文字が 「?」文字に変換されてしまう(下図参照)。上図の、コメントアウトした緑色の文字列の第一行目は正常な文字列で表示される。第2行目は「日」と「人」は康煕部首文字であるため、「?」で表示されてしまう。このため、コード内で康煕部首文字を文字として定数にすることはできない。
この文字のUnicodeを数値で定数にすることはできるかもしれないが、そうすると可読性が無いため、バグの危険性が高まる。
康煕部首変換テーブルをテキストファイル化して、そこの文字をUnicodeとして参照する仕掛けになりそうだ。
4.Unicodeを認識できる関数AscW()の検討
苦労して調べた結果、AscW()が使えることが分かった。AscW()は、Visual Basic for Applications (VBA)の関数である。ゆえに、WordでもExcelでも使える。
ただし、「Visual Basic for the Macintosh では、Unicode 文字列をサポートしていません。」など使用上の制約はある。
https://docs.microsoft.com/ja-jp/office/vba/api/overview/language-reference
サンプルプログラムは下記のとおりである。
Dim s As String
Selection.MoveRight Unit:=wdCharacter, Count:=5, Extend:=wdExtend
s = Selection.Text
Dim lg As Long
For lg = 1 To Len(s)
Dim c As String
c = Mid(s, lg, 1)
Debug.Print c; AscW(c); Hex(AscW(c))
Next
MsgBox "処理終了"
//以上サンプルプログラム
上記サンプルプログラムを下図のWord文書に適用する実験をしてみる。
上図のように、第1行目(「1:日本人」)の先頭にポインタを位置付けて、上記サンプルプログラムを実行する。
Microsoft VBAのメニュー>「表示」>「イミディエイトウィンドウ」で、「イミディエイト」Windowを表示できる。
実行結果は下図のとおりである。期待どおりの値を取得できることを確認する。
次に、第3行目の先頭にポインタを持っていってプログラムを実行する。結果は下記のとおりである。
3-237 FF13
:-230 FF1A
? 12103 2F47
本 26412 672C
? 12040 2F08
「日」と「人」は康煕部首文字であるため「?」文字で表示されているものの、Unicodeの数値は正常に取得できていることがわかる。
康煕部首文字はエディタ上では?文字に変換・表示されてしまうものの、この現象がAscW()の内部動作までは影響していないことが確認された。
5.Windowsの「デスクトップ」からファイルを読み込む
PDF に謎の漢字が含まれるときに、康煕部首文字と正常文字との「変換表」が掲示されている。この対応表はテキストデータであるため可読性がある。
このデータを自分のPCにテキストファイルとして保存して使えば良い。
一時的に使うだけなので、Windowsの「デスクトップ」に「勇者ああああ.txt」ファイルとして保存した。
さて、Windowsの「デスクトップ」からファイルを読み込むにはどうすればよいのか?
解答は次のサイトに掲載されている。
Windows Script Host (WSH) はWindows 管理ツールの 1 つだ。2009年に掲載された情報がまだ生きているのですね。
//以下サンプルプログラム
Dim sFileName As String
Dim WSH As Object 'Windows Script Host
Set WSH = CreateObject("WScript.Shell")
sFileName = WSH.SpecialFolders("Desktop") + "\勇者ああああ.txt"
Debug.Print sFileName//以上サンプルプログラム
イミディエイトウィンドウでの表示は次のとおりである。
C:\Users\YesIam\Desktop\勇者ああああ.txt
6.UTF-8形式のテキストファイルから読み込む
UTF-8形式のテキストファイルから読み込む方法は、そのまんま次のサイトに紹介されている。
//以下サンプルプログラム
Dim sInput As String
With CreateObject("ADODB.Stream")
.Charset = "UTF-8"
.Open
.LoadFromFile sFileName
sInput = .ReadText
.Close
End With
Debug.Print sInput
//以上サンプルプログラム
イミディエイトウィンドウでの表示は次のとおりである。
{"?":"一","?":"丨","?":"丶","?":"丿",(中略)"?":"龠"}
7.JSON形式データを解析する
PDF に謎の漢字が含まれるときに掲載されている「変換表」のデータがJSONであることは見ればわかる。これを解析する方法は次のサイトを参考にした。
何をやっているのやら、よくわからんが、とにかく動く。
//以下サンプルプログラム
Dim obj As Object
Dim json As Object
Set obj = CreateObject("ScriptControl")
obj.Language = "JScript"
obj.addcode "function jsonParse(s){ return eval( '(' + s + ')' ) ; }"
Set json = obj.CodeObject.jsonParse(sInput)
Dim sKangxiRadicals As String
sKangxiRadicals = ChrW(&H2FBA) '0x2fbaは'馬'を表す康煕部首文字
Dim sCorrectChar As String
sCorrectChar = CallByName(json, sKangxiRadicals, VbGet) 'VbGetは定数
MsgBox sCorrectChar + " 処理終了"
//以上サンプルプログラム
実行結果は下図のとおりである。「馬」が正常に表示されているということは、これが(康煕部首文字ではない)日本語の馬であるということである。
8.JSON形式では使えない
結論から言うと、上記のように、VBAでJSON形式で扱うことは無理があることが分かった。
CallByName()を使えば、この関数の第2引数に対応するデータが簡単に引き出せるのだが、このキー(第2引数に代入するデータ)をあらかじめ知っておかねばならない。
{"あい":"愛","ねこ":"猫"}
上記データを例に言えば、「あい」や「ねこ」というキーを知っていて、それを第2引数に設定すれば、データである「愛」や「猫」がヒットするが、キーとして存在しない値を引数に設定すると、プログラムが実行時エラーを起こす。
また、データ「愛」をキーとして第2引数に設定しても、キー「あい」がヒットするのではなく、実行時エラーを起こす。
キーをあらかじめ知るようにプログラムすれば良いのだが、そんな手間があるのであれば構造体の方が単純だ。
JSON関連のライブラリを導入するような話もあるが、そこまで手間をかけたくない。
eval()が何をやっているのか分からない。次のサイトに実験場がある。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/eval
次のコマンドを入力する。
console.log(eval( '({"あい":"愛","ねこ":"猫"})' ));
出力は次のとおりである。Objectに変換されているのが分かる。
Object { あい: "愛", ねこ: "猫" }
(参考)
入力:console.log(eval( '(["あい","愛"])' ));
出力:Array ["あい", "愛"]
次のサイトに「Object と Map の比較」が書かれてあり、そこから、Objectが簡易なデータベースとして使われていることが分かる。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map
9.Word文書の内容を書き換える
前述の「日本人」という文字列が書かれた文書内の康煕部首文字の「日」を「私が」に「置換」てみる。
//以下サンプルプログラム
Dim sKangxiRadicals As String
sKangxiRadicals = ChrW(&H2F47) '0x2f47は'日'を表す康煕部首文字
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = sKangxiRadicals
.Replacement.Text = "私が"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = False
.MatchFuzzy = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
//以上サンプルプログラム
Selection.Find関係のコードは、Wordのメニューコマンド「表示」>「マクロ」>「マクロの記録」を使えば、自動的に生成してくれる。
実行結果は、次の図のとおりである。「私が本人」に置き換わっている。
10.FontはMicrosoft JhengHei
この康煕部首文字のFontはMicrosoft JhengHeiである(下図参照)。これ以外の文字は「MS 明朝」である。
康煕部首文字のコードを日本語の文字コードに「置換」えても、FontはMicrosoft JhengHeiのままである。「日」を「私が」に置き換えても、「私が」の部分はMicrosoft JhengHeiのである。そして、行間の大きさをWordコマンドで制御できない状況に変わりはない。
康煕部首文字のFont(Microsoft JhengHei)を、他のFont(例えば、MS明朝)に置き換えれば問題は解決する。
ここまで調べて、VBAで、変換プログラムを作った。
しかし、CJK問題があることが分かった。
(参考)文字列がCJK Radicals Supplementに自動変換される件に話が続く...
以上