見た目が同一の文字だけど、文字コードが異なっているという意味で「文字化けの問題」を解決する

見た目が同一の文字だけど、文字コードが異なっているという意味で「文字化けの問題」を解決する


見た目が同一の文字だけど、文字コードが異なっているという意味で「文字化けの問題」を解決する記事です。康煕部首文字関係の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次総合計画 概要



皆さま! よろしければ、本件の社会問題にも関心を持って頂ければ幸いです。
(参考)東大阪市第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

康煕部首文字のコードを日本語の文字コードに「置換」えても、FontはMicrosoft JhengHeiのままである。「日」を「私が」に置き換えても、「私が」の部分はMicrosoft JhengHeiのである。そして、行間の大きさをWordコマンドで制御できない状況に変わりはない。
康煕部首文字のFont(Microsoft JhengHei)を、他のFont(例えば、MS明朝)に置き換えれば問題は解決する。



ここまで調べて、VBAで、変換プログラムを作った。
しかし、CJK問題があることが分かった。

以上