74275


EmEditorでGoogle IMEの再変換機能をマクロで自動化する方法はありますでしょうか？

お世話になっております。

Google IMEの再変換機能で1行ずつ自動で選択して再変換を繰り返すというマクロは可能でしょうか？

例文

「それに、たいしてまけるひとは、おおいので、  
まけのげんいんをぶんせきすることは、いみが、ある。  
ひとつひとつは、つまらないしっぱいでも、  
あつめるとほうそくせいが、みえてくる。  
にほんけいざいは、「ちょうきていたい」に、  
はいったといわれ、そのげんいんは、  
「せいさんせいがひくいからだ」とか  
「いのべーしょんがたりないからだ」といわれる。」

このような短い文章が延々と続きます。

1行全部選択→再変換確定→次の行に移動して1行全文選択→再変換確定→これを延々と繰り返す

このように変換しやすい短く区切ったひらがな文をあらかじめマクロで作っておき、自動で最初から最後まで再変換→確定を繰り返すという方法です。

一発では正確にならず誤変換もありましたが、正確さより一発で自動的に最後まで変換していけることを希望しています。

Google IMEで手動ではかなりうまくいきました。

よろしくお願いいたします。

それに、大して負ける人は、多いので、  
負けの原因を分析することは、意味が、ある。  
一つ一つは、つまらない失敗でも、  
集めると法則性が、見えてくる。  
日本経済は、「長期停滞」に、  
入ったと言われ、その原因は、  
「生産性が低い体」とか  
「イノベーションが足りないからだ」と言われる。」


CC BY-SA 4.0
2021-02-22T14:11:35.957
74208
2021-04-07T03:04:34.767
2021-02-22T14:28:49.277
43999
43999
question
emeditor
EmEditorでGoogle IMEの再変換機能をマクロで自動化する方法はありますでしょうか？


[Google 日本語入力](https://www.google.co.jp/ime/cgiapi.html)
を参照してドキュメント一発変換マクロを作ってみました。

  * マクロ利用法  
目的のドキュメントを開いてマクロを実行する。

  * 仕様、制限  
出力はドキュメントのディレクトリにファイル名 Gconv.txt (OutlPath) として第一候補の一発変換を保存する。  
入出力は UTF-8

[code]

    var OutlPath = "Gconv.txt";
    
    function googleConv( str ){
        var rval = "";
        try {
            var uri = "http://www.google.com/transliterate";
            uri += "?langpair=ja-Hira|ja";
            uri += "&text;=" + encodeURIComponent( str );    // パーセントエンコード
    
            var xhr = new ActiveXObject("Msxml2.ServerXMLHTTP");
            xhr.open( "GET", uri, true );
            xhr.setRequestHeader( "User-Agent", "EmEditor " + editor.Version );     // 必要のない設定
    //      xhr.setProxy( 2, "127.0.0.1:8080", "" );
            xhr.send();
            for( var rty = 1000; xhr.readyState != 4 && rty > 0; --rty ){ xhr.waitForResponse(100); }
            if( xhr.readyState == 4 ){
                if( xhr.status == 200 ){ rval = xhr.responseText; }
            } else {
                xhr.abort();
                alert( "Error abort" );
            }
        } catch (e) {
            alert( "Error( " + e.message + " )" );
        }
        return rval;
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    // MAIN
        var fs = new ActiveXObject( "Scripting.FileSystemObject" );
        var strPath = document.Path + "\\" + OutlPath;
        var fh = fs.OpenTextFile( strPath, 2, true, -1 );
        var docReadOnly = document.ReadOnly;
        document.ReadOnly = true;
    
        document.selection.StartOfDocument();
        for( var yPos = 1; yPos <= document.GetLines(); ++ yPos ){
            var str = document.GetLine( yPos );
            var m = str.match( /^(\s*)(.*?)(\s*)$/ );
            if( m != null && m[2] != "" ){
                str = m[2];
                var gconv = googleConv(str);
                Sleep( 300 );
                if( gconv == "" )   break;  // Error
                var jdata = JSON.parse( gconv );
                str = "";
                for( var i = 0; i < jdata.length; ++ i ){
                    str += jdata[i][1][0];
                }
                fh.WriteLine( m[1] + str + m[2] );
            } else {
                fh.WriteLine( "" );
            }
            document.selection.SetActivePoint( eePosLogical, 1, yPos ); // 実行状況が分かるようにカーソルを動かす。無くても良い。
        }
        fh.Close();
    
        document.selection.StartOfDocument();
        document.ReadOnly = docReadOnly;
    
[/code]


CC BY-SA 4.0
2021-02-23T12:36:26.750
74232
2021-02-23T12:36:26.750
40304
74208
answer


変換エンジンを YahooAPI にして、変換ファイル名の変更と、文字数対策を追加してみました。

  * マクロ利用法  
・このマクロ本体は SJIS で保存。  
・アプリケーションIDを取得してソースリスト先頭の Appid に設定する。  
<https://e.developer.yahoo.co.jp/register>  
例: [Yahoo! ID連携 v1] アプリケーションの種類 クライアントサイド（Yahoo! ID連携 v1）, 利用者情報 個人, アプリケーション名
<適当に>, サイトURL <無記入>, アプリケーションの説明 <無記入>, ガイドライン <同意する>  
・目的のドキュメントを開いてマクロを実行する、変換終了時に自動で変換ファイルをオープンする。

  * 仕様、制限  
・Yahoo の仕様により一行の最大文字数は80文字(ひらがな換算時)まで  
かな漢字変換Web APIは、24時間以内で1つのアプリケーションIDにつき50000件のリクエストが上限
<https://developer.yahoo.co.jp/appendix/rate.html>  
・一行単位で変換、  
一行の文字数が多い場合 "。↓▽▼" で分割して、それを最大文字数以内に収まる範囲で連結して処理、  
それでも文字数が多い場合 "、, " で更に分割して、それを最大文字数以内に収まる範囲で連結して処理、  
それでも最大文字数を超える場合 EmEditorのアウトプットバーに行番号を出力。  
・出力はドキュメントのディレクトリにファイル名 "再変換済み +{ドキュメントのファイル名}" として第一候補の連結文字列(一発変換)を出力。  
・入出力は UTF-8

[code]

    #title "one-hit wonder"
    #tooltip "一括漢字変換"
    
    var Appid = ""; // *** Yahoo APPID ***
    var RequestWait = 200;      // リクエスト間隔制限
    var SizeLimit = 80;         // 変換文字数制限
    
    var flagOutputBar = OutputBar.Visible;
    var recCount = 0;   // リクエスト回数カウンター
    
    function putOutBar( num, mes ){
        OutputBar.Visible = true;
        OutputBar.writeln( num + ": " + mes );
        flagOutputBar = true;
    }
    
    // デベロッパーネットワークトップ > テキスト解析 > かな漢字変換
    // https://developer.yahoo.co.jp/webapi/jlp/jim/v1/conversion.html
    function yahooApi( str ){
        if( Appid == "" )   return "";
        recCount ++;
    //  return str + "||";          // DEBUG
        var rval = "";
        try {
            var uri = "https://jlp.yahooapis.jp/JIMService/V1/conversion";
            uri += "?appid=" + Appid;
            uri += "&sentence=" + encodeURIComponent( str );
    
            var xhr = new ActiveXObject("Msxml2.ServerXMLHTTP");
            xhr.open( "GET", uri, true );
            xhr.setRequestHeader( "User-Agent", "EmEditor " + editor.Version + "; Yahoo AppID " + Appid );
    //      xhr.setProxy( 2, "127.0.0.1:8080", "" );
            xhr.send();
            for( var rty = 1000; xhr.readyState != 4 && rty > 0; --rty ){ xhr.waitForResponse(100); }
            if( xhr.readyState == 4 ){
                if( xhr.status == 200 ){
                    var dom = new ActiveXObject("Msxml2.DOMDocument");
                    dom.async = false;
                    dom.loadXML( xhr.responseText );
                    var segs = dom.getElementsByTagName("Segment");
                    for( var i = 0; i < segs.length; i++ ) {
                        var para = segs[i].getElementsByTagName("Candidate");
                        rval += para[0].text;
                    }
                } else if( xhr.status == 403 ){
                    alert( "Web APIのリクエスト回数制限に到達、00:00にリセット" );
                    ravl = "\x08";
                }
            } else {
                xhr.abort();
                alert( "Error abort" );
                ravl = "\x08";
            }
        } catch (e) {
            alert( "Error( " + e.message + " )" );
            ravl = "\x08";
        }
        return rval;
    }
    
    // 簡易文節分割
    function splitSeg( rg, str ){   // rg = /^(.*?[。↓▽▼])(.+)$/,  /^(.*?[、　 ])(.+)$/
        var para = [];
        var wrk = str;
        var m;
        while( m = wrk.match( rg ) ){
            if( m[1] != "" ){ para.push( m[1] ); }
            wrk = m[2];
        }
        if( wrk != "" ){ para.push( wrk ); }
        return para;
    }
    
    // 変換処理本体
    function comv_main( strPath ){
        var fh = new ActiveXObject("ADODB.Stream");
        fh.Type = 2;
        fh.Charset = "UTF-8";
        fh.Open();
    
        document.selection.StartOfDocument();
        for( var yPos = 1; yPos <= document.GetLines(); ++ yPos ){
            document.selection.SetActivePoint( eePosLogical, 1, yPos ); // 実行状況が分かるようにカーソルを動かす。無くても良い。
            var str = document.GetLine( yPos );
            var m = str.match( /^(\s*)(.*?)(\s*)$/ );
    //      if( m != null && m[2] != "" ){
            if( m[2] != "" ){
                str = m[2];
    //          alert( str.length );
                if( str.length >= SizeLimit ){
                    var wrk = "";
    
                    var para2 = splitSeg( /^(.*?[。↓▽▼])(.+)$/, str );       // 分割文字 '。','↓','▽','▼'
                    var str3 = "";
                    for( var i = 0; i < para2.length; ++ i ){
                        str3 += para2[i];
                        if( i+1 < para2.length && str3.length + para2[i+1].length < SizeLimit ){ continue; }
    
                        if( str3.length >= SizeLimit ){
                            var para3 = splitSeg( /^(.*?[、　 ])(.+)$/, str3 );       // 分割文字 '、','　',' '
                            var wrk4 = "";
                            for( var j = 0; j < para3.length; ++ j ){
                                wrk4 += para3[j];
                                if( j+1 < para3.length && wrk4.length + para3[j+1].length < SizeLimit ){ continue; }
                                if( wrk4.length >= SizeLimit ){
                                    putOutBar( yPos, "SizeOver '" + wrk4 + "'" )
                                }
                                var yconv = yahooApi( wrk4 );
                                if( yconv == "\x08" ){ return; }    // Error
                                Sleep( RequestWait );
                                wrk4 = "";
                                wrk += yconv;
                            }
                        } else {
                            var yconv = yahooApi( str3 );
                            if( yconv == "\x08" ){ return; }    // Error
                            Sleep( RequestWait );
                            wrk += yconv;
                        }
                        str3 = "";
                    }
                    str = wrk;
                } else {
                    var yconv = yahooApi( str );
                    if( yconv == "\x08" ){ return; }    // Error
                    Sleep( RequestWait );
                    str = yconv;
                }
                fh.WriteText( m[1] + str + m[3], 1 );
            } else {
                fh.WriteText( "", 1 );
            }
        }
        fh.SaveToFile( strPath, 2 );
        fh.Close();
        return 0;
    }
    
    //////////////////////////////////////////////////////////////////////////////
    // MAIN
        var strPath = document.Path + "\\再変換済み　" + document.Name;
        OutputBar.Clear();
        OutputBar.Visible = true;
        var docReadOnly = document.ReadOnly;
        document.ReadOnly = true;
    
        comv_main( strPath );
    
        document.selection.StartOfDocument();
        document.ReadOnly = docReadOnly;
        OutputBar.writeln( "\n*** API リクエスト回数 : " + recCount ); flagOutputBar = true;   // 不要ならこの行を削除
        if( ! flagOutputBar ){ OutputBar.Visible = false; }
        editor.OpenFile( strPath, 0, eeOpenAllowNewWindow );    // 変換ファイルを開く
    
    
[/code]


CC BY-SA 4.0
2021-02-25T11:30:05.320
74275
2021-02-25T11:30:05.320
40304
74208
answer


Microsoft IME の利用法が分かったのでプログラムを書いてみました。  
独立したアプリケーションとして動作します。  
中心部分は参照した [Netplanetes Memo](https://www.pine4.net/Memo/Article/Archives/424)
ほぼそのままです。

  * 利用法  
汎用のテキスト処理プログラムにしましたが、コマンドプロンプトで実行する以外に、  
EmEditor のツールに、引数 `-k $(CurText)` とか `-k -f $(Path)` などとして、  
アウトプット バーを使用するにチェックすれば、選択文字やファイル全体の処理を EmEditor と連携できます。

  * 仕様  
入力は [ 標準入力、コマンドライン、クリップボード、ファイル ] から選べて  
出力は [ 標準出力、クリップボード、ファイル ] から選べます。  
一発変換の変換精度は Microsoft IME の辞書次第です。

### コンソール アプリ (.NET Framework)

[code]

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Windows;
    
    namespace ImeConversion {
        // IFELanguage2 Interface ID
        //[Guid("21164102-C24A-11d1-851A-00C04FCC6B14")]
        [ComImport]
        [Guid( "019F7152-E6DB-11d0-83C3-00C04FDDB82E" )]
        [InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]
        public interface IFELanguage {
            int Open();
            int Close();
            int GetJMorphResult( uint dwRequest, uint dwCMode, int cwchInput, [MarshalAs( UnmanagedType.LPWStr )] string pwchInput, IntPtr pfCInfo, out object ppResult );
            int GetConversionModeCaps( ref uint pdwCaps );
            int GetPhonetic( [MarshalAs( UnmanagedType.BStr )] string @string, int start, int length, [MarshalAs( UnmanagedType.BStr )] out string result );
            int GetConversion( [MarshalAs( UnmanagedType.BStr )] string @string, int start, int length, [MarshalAs( UnmanagedType.BStr )] out string result );
        }
    
        class Program {
            static IFELanguage ifelang = null;
    
            [STAThread]
            // 漢字 -> ひらがな
            static private string immYomi( in string str )
            {
                string result;
                int hr = ifelang.GetPhonetic( str, 1, -1, out result );
                if( hr != 0 ) {
                    throw Marshal.GetExceptionForHR( hr );
                }
                return result;
            }
    
            // ひらがな -> 漢字
            [STAThread]
            static private string immKanji( in string str )
            {
                string result;
                int hr = ifelang.GetConversion( str, 1, -1, out result );
                if( hr != 0 ) {
                    throw Marshal.GetExceptionForHR( hr );
                }
                return result;
            }
    
            [STAThread]
            static int Main( string[] args )
            {
                var opt_k = false;  // -k 漢字変換     ひらがな -> 漢字 -k (変換文字列, -f ファイルパス)
                var opt_h = false;  // -h ひらがな変換 漢字 -> ひらがな -h (変換文字列, -f ファイルパス)
                var opt_co = false; // -co 結果をクリップボードへ出力
                var opt_ci = false; // -ci クリップボードから opt_str を設定
                var opt_f = "";     // -f ファイル変換 ファイルパス
                var opt_o = "";     // -o 出力 ファイルパス
                var opt_str = "";   // 変換元文字列
                Encoding fileEnc = new UTF8Encoding( encoderShouldEmitUTF8Identifier: false );  // ファイルエンコーディング -utf8, -sjis, -eucjp
                for( int i = 0; i < args.Length; ++ i ) {
                    bool f_last = (i >= args.Length - 1);
                    var sOpt = Regex.Replace( args[i], "^/", "-" ).ToLower();
                    if( sOpt == "-k" ) {    opt_k = true;
                    } else
                    if( sOpt == "-h" ) {    opt_h = true;
                    } else
                    if( sOpt == "-sjis" ) { fileEnc = Encoding.GetEncoding( "Shift_JIS" );
                    } else
                    if( sOpt == "-utf8" ) { fileEnc = new UTF8Encoding( encoderShouldEmitUTF8Identifier: false ); ;
                    } else
                    if( sOpt == "-eucjp" ) {fileEnc = Encoding.GetEncoding( "euc-jp" );
                    } else
                    if( sOpt == "-co" ) {   opt_co = true;
                    } else
                    if( sOpt == "-ci" ) {   opt_ci = true;
                    } else
                    if( sOpt == "-f" && ! f_last ) {    opt_f = args[++i];
                    } else
                    if( sOpt == "-o" && ! f_last ) {    opt_o = args[++i];
                    } else
                    if( sOpt == "-?" ) { // Help
                        opt_k = opt_h = opt_co = opt_ci = false;
                        opt_str = opt_f = "";
                        break;
                    } else
                    if( f_last ) {
                        opt_str = args[i];
                    } else {
                        Console.WriteLine( " option ? : " + args[i] );
                        opt_k = opt_h = opt_co = opt_ci = false;
                        opt_str = opt_f = "";
                        break;
                    }
                }
                if( opt_ci ) {
                    opt_str = Clipboard.GetText();
                }
                if( ! (opt_k ^ opt_h) || (opt_str == "" && opt_f == "") || (opt_ci && opt_f != "") ) {
                    Console.WriteLine(
                        "Usage: (-k,-h) [-utf8,-sjis,-eucjp] [-o file path] (string, -f file path)\n" +
                        " -k : ひらがな -> 漢字\n" +
                        " -h : 漢字 -> ひらがな\n" +
                        " -ci : クリップボード から変換元を取得\n" +
                        " -co : 結果をクリップボードへ出力\n" +
                        " -f file path : 変換元のファイルパス、'-f stdin:'で標準入力\n" +
                        " -o file path : -f|-ci 指定時の出力ファイルパス、指定しなければ標準出力。エンコードは入力ファイルの設定に合わせる\n" +
                        " -utf8,-sjis,-eucjp : 変換元のファイルのエンコード。省略時は utf8\n" +
                        " \"string\" : 変換元の文字列\n" +
                        "\n" +
                        "  -k,-h どちらか一方だけを必ず指定\n" +
                        "  -utf8,-sjis,-eucjp 最も最後の指定が有効\n" +
                        "  string, -ci, -f file path どれかを必ず指定\n" +
                        "  string を指定する場合は必ず最後に指定、これ以外のオプションは順不同"
                    );
                    return 1;
                }
    
                try {
                    ifelang = Activator.CreateInstance( Type.GetTypeFromProgID( "MSIME.Japan" ) ) as IFELanguage;   // "MSIME.Japan.FELang", "MSIME.Japan"
                    int hr = ifelang.Open();
                    if( hr != 0 ) {
                        throw Marshal.GetExceptionForHR( hr );
                    }
                    if( opt_f == "" ) {
                        var str = ( opt_h )? immYomi( opt_str ) : immKanji( opt_str );
                        if( opt_co ) {
                            Clipboard.SetText( str );
                        } else
                        if( opt_o != "" ) {
                            using( StreamWriter fh = new StreamWriter( opt_o, false, fileEnc ) ) {
                                fh.WriteLine( str );
                            }
                        } else {
                            Console.WriteLine( str );
                        }
                    } else {
                        bool f_stdin = (opt_f.ToLower() == "stdin:");
                        if( ! File.Exists( opt_f ) && ! f_stdin ) {
                            Console.WriteLine( "指定されたファイルが見つかりません。 '" + opt_f + "'" );
                            return 1;
                        }
                        bool f_cons = (opt_o == "" && ! opt_co);
                        var memBuf = new MemoryStream();
                        var conEnc = Console.OutputEncoding;
                        if( conEnc == Encoding.UTF8 ) {
                            conEnc = new UTF8Encoding( encoderShouldEmitUTF8Identifier: false );    // BOMなし
                        }
                            using( var sr = (f_stdin)? Console.In : new StreamReader( opt_f, fileEnc ) ) {
                            StreamWriter writer =
                            ( f_cons )?
                            new StreamWriter( Console.OpenStandardOutput(), conEnc ) :
                            ( ( opt_co )?
                                new StreamWriter( memBuf ) :
                                new StreamWriter( opt_o, false, fileEnc )
                            );
                            writer.AutoFlush = true;
    
                            var line = "";
                            var str = "";
                            var no = 1;
                            while( (line = sr.ReadLine()) != null ) {   // 行単位で処理
                                str = (line.Trim() == "")?
                                    "" :
                                    ( (opt_h)? immYomi( line ) : immKanji( line ) );
                                writer.WriteLine( str );
                                if( ! f_cons ) {
                                    Console.Write( no.ToString() + "\r" );
                                }
                                ++no;
                            }
    
                            if( opt_co ) {
                                writer.Flush();
                                Clipboard.SetText( Encoding.UTF8.GetString( memBuf.ToArray() ) );
                            }
                            if( ! f_cons ) {
                                writer.Close();
                            }
                        }
                    }
                } catch( COMException ex ) {
                    Console.WriteLine( "*** Error!! ***\n  " + ex.Message );
                    return 1;
                } finally {
                    if( ifelang != null ) ifelang.Close();
                    ifelang = null;
                }
                return 0;
            }
        }
    }
    // 参照
    // Microsoft IME の IFELanguage を使用して 読み仮名から漢字、漢字から読み仮名変換を行う簡易プログラム - Netplanetes
    //    https://www.pine4.net/Memo/Article/Archives/424
    //
    
[/code]

  * ビルド手順 

Visual Studio 2019 辺りで、新規に C# のコンソ－ルアプリ(.NET Framework) のプロジェクトを作成 (プロジェクト名は
ImeConversion など適当に)  
Program.cs に上のソースを入れ替えて、namespace をプロジェクト名に合わせて書き換えて、他は標準設定のままでもビルドできます。


CC BY-SA 4.0
2021-03-20T11:25:25.527
74783
2021-04-07T03:04:34.767
2021-04-07T03:04:34.767
40304
40304
74208
answer