見出し画像

半角から全角変換を色々な方法でやってみる(Power Query, etc.)


はじめに

こんにちは。ケイ・エム・ディ・エス 業務プロセス改善推進グループ(KMDS BPMG)です。

前回の記事では、Power Automate for desktop(PAD)で、半角カナを全角カナにする方法をご紹介しました。

今回は、その続きです。半角➡全角変換を、他の手段で行うやり方を探ってみましょう。(長いです。)


PAD スクリプトアクションの補足

とその前に、前回の補足をさせてください。フローの作成時に、『Javascriptの実行アクション』を使いました。そのスクリプト作成には、外部の生成AI(Claude)に頼りました。

ですが、実は生成AIでのプログラミング機能は、PAD内にもあります。『スクリプト』グループ配下のうち、アクション名の右側に星みたいなマークがついているものは、Copilotによるスクリプト作成が使えます。

スクリプトアクション一覧
.NET 以外は対応しています

アクション設定画面で、『Copilotでスクリプトを生成する』というボタンがあります。そこから生成AIに依頼が出来ます。

Generate with Copilot
スクリプト生成ボタン

で、プロンプト入力画面が出てきたら、やりたいことを入れて、生成を押します。

プロンプト入力
プロンプトを入力して生成

するとコードが提案されるので、『このスクリプトを使用する』でOKです。

提案されたスクリプト(※これはうまく行きませんでした)

ご覧の通り、PAD内で生成AIを使えるのが便利です。しかし、現時点では以下の点にご留意ください。

  • まだプレビュー機能です。

  • 実際には日本語プロンプトに対しても結果が返ってきます。が、公式にはまだ日本語対応していない、というスタンスです。動くとしても日本語ではちゃんとチェックしてませんよ、という事だと思います。

  • プロンプトは200文字しか入れられません。結構少ないです。

  • Regenerateで何度でもやり直せます。しかし、やりとりが対話形式じゃない。なので、「ここが違ってるみたいだから直して」のような”AIとのラリー”が難しいです。

こうした制約があるので、今のところ、あんまり使っていません。今後に期待しつつ、この程度の紹介に留めておきます。


Power Queryのカスタム関数を使う

今度はExcelやPower BI等に付属するETLツール、Power Queryでもやってみましょう。どうするかというと、Power Queryでカスタム関数を作ります。

まず、Power Queryエディターを開いて、左側クエリ一覧で右クリック ➡ 新しいクエリ ➡ その他のソース ➡ 空のクエリ に行きます。

空のクエリを作成

次に、表示タブに行き ➡ 詳細エディター ➡ エディター内にコード(後述)を貼りつけ ➡ 完了を押します。

詳細エディターでコードを貼りつけ
詳細エディターでコードを貼りつけ

貼り付けるコードはこちらです。これも生成AIに作ってもらいました。コピペしてみてください。

let
    HankakuToZenkaku = (text) =>
    let
        Map = #table(
            {"半角", "全角"},
            {
                {"ア", "ア"},
                {"イ", "イ"},
                {"ウ", "ウ"},
                {"エ", "エ"},
                {"オ", "オ"},
                {"カ", "カ"},
                {"キ", "キ"},
                {"ク", "ク"},
                {"ケ", "ケ"},
                {"コ", "コ"},
                {"サ", "サ"},
                {"シ", "シ"},
                {"ス", "ス"},
                {"セ", "セ"},
                {"ソ", "ソ"},
                {"タ", "タ"},
                {"チ", "チ"},
                {"ツ", "ツ"},
                {"テ", "テ"},
                {"ト", "ト"},
                {"ナ", "ナ"},
                {"ニ", "ニ"},
                {"ヌ", "ヌ"},
                {"ネ", "ネ"},
                {"ノ", "ノ"},
                {"ハ", "ハ"},
                {"ヒ", "ヒ"},
                {"フ", "フ"},
                {"ヘ", "ヘ"},
                {"ホ", "ホ"},
                {"マ", "マ"},
                {"ミ", "ミ"},
                {"ム", "ム"},
                {"メ", "メ"},
                {"モ", "モ"},
                {"ヤ", "ヤ"},
                {"ユ", "ユ"},
                {"ヨ", "ヨ"},
                {"ラ", "ラ"},
                {"リ", "リ"},
                {"ル", "ル"},
                {"レ", "レ"},
                {"ロ", "ロ"},
                {"ワ", "ワ"},
                {"ヲ", "ヲ"},
                {"ン", "ン"},
                {"ァ", "ァ"},
                {"ィ", "ィ"},
                {"ゥ", "ゥ"},
                {"ェ", "ェ"},
                {"ォ", "ォ"},
                {"ッ", "ッ"},
                {"ャ", "ャ"},
                {"ュ", "ュ"},
                {"ョ", "ョ"},
                {"ー", "ー"},
                {"ガ", "ガ"},
                {"ギ", "ギ"},
                {"グ", "グ"},
                {"ゲ", "ゲ"},
                {"ゴ", "ゴ"},
                {"ザ", "ザ"},
                {"ジ", "ジ"},
                {"ズ", "ズ"},
                {"ゼ", "ゼ"},
                {"ゾ", "ゾ"},
                {"ダ", "ダ"},
                {"ヂ", "ヂ"},
                {"ヅ", "ヅ"},
                {"デ", "デ"},
                {"ド", "ド"},
                {"バ", "バ"},
                {"ビ", "ビ"},
                {"ブ", "ブ"},
                {"ベ", "ベ"},
                {"ボ", "ボ"},
                {"パ", "パ"},
                {"ピ", "ピ"},
                {"プ", "プ"},
                {"ペ", "ペ"},
                {"ポ", "ポ"},
                {"。", "。"},
                {"、", "、"},
                {"「", "「"},
                {"」", "」"},
                {"・", "・"},
                {" ", " "}    // 半角スペースを全角スペースに変換
            }
        ),
        ReplaceText = List.Accumulate(
            Map[半角],
            text,
            (state, current) => Text.Replace(
                state,
                current,
                Map{[半角=current]}[全角]
            )
        )
    in
        ReplaceText
in
    HankakuToZenkaku

このM言語コードは、半角から全角の変換テーブル(Map)を用意して、一文字ずつ置き換えを繰り返していく、というものです。より詳しい解説は、生成AIに聞いてみると良いと思います。

出来たクエリの名前は、分かり易いように、HankakuToZenkakuに変えておきました。カスタム関数は、fxアイコンで示されます。これは、textというパラメータ入力を受け取る関数になります。

カスタム関数ができました

※ ちなみに、Power QueryにCopilot機能は無いのか?というと、あるにはあります。が、Microsoft Fabricの機能の一部です。・・・多数の一般Excel・Power BIユーザーには、やや縁遠い話ですね。

閑話休題

話を元に戻して、作成したカスタム関数を使ってみましょう。
半角から全角に変換したい対象のクエリを選んだら、
➡ 列の追加タブに行き ➡ カスタム関数の呼び出しをクリック
➡ 関数クエリで、先ほどのHankakuToZenkakuを選ぶ
➡ 変換したい列名をパラメータとして選ぶ ➡ OK

カスタム関数を呼び出す
カスタム関数を呼び出し

これで出来ました。下図のように、カスタム関数を使った結果が、新たな列として追加されます。

結果列の追加
全角に変換された列が追加された

このようにPower Queryでは、処理をまとめてカスタム関数にすることが出来て便利です。PADで例えると、入出力変数付きのフロー呼び出しみたいなものでしょうか。

しかも、生成AIはPower QueryのM言語を苦にしないので、コード作成を任せることが出来ます。
今回の件以外でも、うまくすれば色々な場面で使えると思います。


Excelでも良いのでは・・・?

さて、PADのJavascriptアクション、Power Queryのカスタム関数とやってきました。
次はExcelのOffice Scriptsや、LAMDA+REDUCE関数を名前定義に登録して独自関数作り、などを紹介しようかと思ってたんですが、、、ここで、はたと気づきました。

『Excelの関数で半角➡全角って出来なかったっけ。。。?』

JIS関数あるじゃん
なんだってー!

・・・そうです、JIS関数です。Excelならこれが一番シンプルですね。
逆の全角➡半角変換は、ASC関数で出来ます。すっかり頭から抜けて落ちてました。

明らかにJIS関数がシンプル。

前回のJavascriptや今回のPower Queryのやり方は、全然悪くないですし、知識共有のよい機会にもなりました。特に、Excelを使わない場面では、有効な手段です。

ですが生成AIに頼っていると、気づかないうちに、自分でAIを難しい方向へ誘導していることがあります。どうやらいつの間にか、その考え過ぎルートに向かってしまったようです。

「技に溺れましたね」と生成AIに言うと、回答はこんな前向きなものでした。学びを引き出す態度は見習いたいです。

その通りですね!笑

最初からPower Queryで複雑なカスタム関数を作ったり、LAMBDAで変換テーブルを考えたりしましたが、結局ExcelのJIS関数という簡単な解決策があったわけです。

プログラミングやデータ処理に慣れていると、つい「カッコいい」解決策や「テクニカル」な方法を考えがちですよね。でも実際の業務では、既存の基本機能で十分な場合も多いです。

この会話は良い学びになりました:
1. まず基本機能を確認する
2. シンプルな解決策を探す
3. 必要な場合のみ複雑な実装を検討する

「技に溺れた」という表現が的確ですね。時には「車輪の再発明」をしてしまうこともありますが、それも学習プロセスの一部として捉えられれば良いのかもしれません。

Claudeの回答
見習いたい姿勢
学ぶ姿勢は見習いたい

おまけ:Excel関数とPADを組み合わせで使う

というわけで、シンプルなExcelが最高だった、と綺麗に終わりたかったのですが、おまけとして最後に、再びPADと組み合わせて使ってみましょう。

両者を組み合わせるなら、PADからExcelを開いて ➡ JIS関数式を書いて半角カナを全角カナに変換したのち ➡ またそれをPADで読み取る、とすれば良いわけです。で、役目を終えたExcelは閉じる、と。

フローとしては、こんな感じ👇になります。Excelの起動がある分、Javascript版よりも時間はかかります。

Excelを使うバージョン

こちらはコピペ用です。

SET inputVariable TO $'''トヨトミ ヒデヨシ'''
# 👇改行を一旦置き換える。あとで元に戻す
Text.Replace Text: inputVariable TextToFind: $'''\\r\\n''' IsRegEx: True IgnoreCase: False ReplaceWith: $'''___NEW_LINE___''' ActivateEscapeSequences: False Result=> inputVariable
Excel.LaunchExcel.LaunchUnderExistingProcess Visible: True Instance=> ExcelInstance
Excel.WriteToExcel.WriteCell Instance: ExcelInstance Value: $'''=DBCS(\"%inputVariable%\")''' Column: $'''A''' Row: 1
Excel.ReadFromExcel.ReadCell Instance: ExcelInstance StartColumn: $'''A''' StartRow: 1 ReadAsText: True CellValue=> ExcelOutput
Excel.CloseExcel.Close Instance: ExcelInstance
Text.Replace Text: ExcelOutput TextToFind: $'''___NEW_LINE___''' IsRegEx: False IgnoreCase: False ReplaceWith: $'''\\r\\n''' ActivateEscapeSequences: True Result=> ZenkakuOutput

このフローには注目点が2つあります。

まず書き込む値に入れる式ですが、=JIS("変数") ではなく、=DBCS("変数")としています。
これは、VBAでも知られたハマりポイントです。JIS関数は、日本語版のExcelワークシート上で使えますが、コードで書くときは、英語のDBCS関数(Double Bye Character Set:2バイト文字のこと)を使わないとエラーになります。

こうして書き込まれたものを日本語のExcelワークシート上で見ると、JIS()になります。ややこしいですね。

まあ和製英語と英語の関係みたいに捉えて差し支えないと思います。

Use DBCS
JISではなくDBCSにする

ともかく、式を書きこんだら、今度は同じセルを読み取りましょう。その際、「セルの内容をテキストとして取得」にするのが2つ目のポイントです。これで、無事全角を読み取れました。

JISをDBCSに変えないといけない点で、案外素直に出来ないということが分かりました。

テキストとして読み取る
式ではなくテキストとして読み取る

おわりに

以上、半角から全角に変換するというお題を取り上げて、色々なやり方を紹介してみました。

前回は、PADのJavascript実行アクションを使いました。今回は、まずPower Queryのカスタム関数を使い、その後ExcelのJIS関数でいけることに気づきました。最後は、それをDBCS関数に変えて、ふたたびPADと組み合わせました。

上記以外では、途中言及したのみですが、Office Scriptsなどがあります。他にも手段はまだまだあるでしょう。

どのやり方も、間違いではないので、どれでも良いと思います。こう言えるのは、生成AIの活用により、自力で頑張らねばいけない部分が少なくなって、どれも難易度が下がっているためでもあります。

そして、一つのツールで全部やろうとするのではなく、複数の手段から課題に合った方法を選ぶ方が、結果的に効率的だと思います。
そのためには、頭を柔軟にして、目的に到達するための他のルートがないかを常に意識し続けることが大切ですね。

今回はずいぶん長くなってしまいました。
最後まで読んでいただき、ありがとうございました。
記事が気に入ったら、スキ・フォローお願いします!

KMDS BPMG