指定した条件にあたる文字列を検索し、該No.10159
tack さん 23/05/31 20:35
 
お世話になっております、先日はご教授いただきありがとうございました。

標記の件のように、該当する文字列を検索し、その上でさらに別の条件を検索しマッ
チした部分に置換や代入は難しいでしょうか。

jsをChatGPTにて説明してもらいつつ組んでみましたが上手くいかなかったので出来
るのかも確認したく伺います。

【例文】
"太郎",
"花子",
"カズヒロ",
"アーサー",

"2" "こんにちわ"
"2" "ぼくは太郎だよ!"
"3" "私は花子です"
"9" "俺はカズヒロだ"
"4" "ミーはアーサーです"
"3" "今日は暑いね"
"4" "日本の夏は暑いよね…"

【変換後内容】
"太郎" "こんにちわ"
"太郎" "ぼくは太郎だよ!"
"花子" "私は花子です"
"カズヒロ" "俺も元気だな"
"アーサー" "ミーはアーサーです"
"花子" "今日は暑いね"
"アーサー" "日本の夏は暑いよね…"

【目的】
最初に名前が並んでいるので、取得し、下記の文の"2"などランダムに振られた数字
の部分に置換もしくは挿入をしたいのです。
太郎が"2"なら、文章中の"2"は全て太郎になります。
ただ"数字"もランダムに設定されるため、"数字"も検索にて求めたいのです。

検索までは

gofiletop;
jsmode "WebView2";

js {
  let data = hidemaru.getSelectedText() || hidemaru.getTotalText();
  let lines = data.split('\n');
  let result = "";

  let names = [];

  for (let line of lines) {
    let nameMatch = line.match(/"(.*?)",/);
    if (nameMatch) {
      let name = nameMatch[1];
      names.push(name);
      hidemaru.message(`名前を取得しました: ${name}`);

でいけるのですが、以降が全然思いつかず困っています。こういった場合は配列を使
うのでしょうか?

[ ]
RE:10159 指定した条件にあたる文字列を検No.10162
こみやんま さん 23/05/31 23:05
 
>【例文】
>"太郎",
>"花子",
>"カズヒロ",
>"アーサー",
>
>"2" "こんにちわ"
>"2" "ぼくは太郎だよ!"
>"3" "私は花子です"
>"9" "俺はカズヒロだ"
>"4" "ミーはアーサーです"
>"3" "今日は暑いね"
>"4" "日本の夏は暑いよね…"


このような「名前が出ていれば、それは自己を指し示す」といったものであれば、
単純な抽出も可能でしょうが、そうでない場合、
ある程度会話を咀嚼できるエンジンが必要となります。

例えば、

"2" "こんにちわ"
"2" "ぼくは太郎だよ!"
"3" "太郎、私は花子です"
"9" "俺はカズヒロだ。太郎、花子よろしく!"
"4" "カズヒロとは以前会ったね。太郎、花子、はじめまして、ミーはアーサーです"
"3" "今日は暑いね"
"4" "日本の夏は暑いよね…"

といった会話内容になるだけでも難易度がどかんと跳ね上がります。

よって会話データに対して、ChatGPTなりで、

------------------------------------------------------
以下のようなデータがあります。
"2"や"9"などの最初の""で囲まれた数字は、
太郎 花子 カズヒロ アーサー のいずれを指し示す番号だとすると、

各行の会話主は誰ですか?
名前だけで答えてください。

"2" "こんにちわ"
"2" "ぼくは太郎だよ!"
"3" "太郎、私は花子です"
"9" "俺はカズヒロだ。太郎、花子よろしく!"
"4" "カズヒロとは以前会ったね。太郎、花子、はじめまして、ミーはアーサーです"
"3" "今日は暑いね"
"4" "日本の夏は暑いよね…"
------------------------------------------------------

などとして聞いて、各行の会話主が誰なのかのデータを作ってもらうか
もしくは、各番号が誰かのデータを作ってもらう必要があるでしょう。


[ ]
RE:10159 指定した条件にあたる文字列を検No.10163
igus さん 23/05/31 23:25
 
//$n:名前候補
//$b:置換前数字
//$a:置換後名前
#x=x;#y=y;#i=0;#j=0;#k=0;begingroupundo;gofiletop;
while (1){
  searchdown @"(?<="").*?(?="",)",regular;
  if (!result) break;
  $n[#i]=gettext(foundtopx,foundtopy,foundendx,foundendy);
  #i=#i+1;
}
while(#j<#i){
  gofiletop;
  searchdown2 @"^""\d+"" .*"+$n[#j]+".*",regular;
  golinetop;
  searchdown2 @"^""\d+""",regular;
  $b[#j]=gettext(foundtopx,foundtopy,foundendx,foundendy);
  $a[#j]=@""""+$n[#j]+@"""";
  //message $b[#j];
  //message $a[#j];
  #j=#j+1;
}
while(#k<#j){
  replaceallfast $b[#k],$a[#k];
  #k=#k+1;
}
moveto #x,#y;endgroupundo;

[ ]
RE:10163 指定した条件にあたる文字列を検No.10164
こみやんま さん 23/06/01 00:21
 
この例は、「はじめての名前が出てきたら、それは当人の会話」といったことに依存
するので、

例であげている「そんな会話ある?」みたいな
恐ろしく単純な文面のうちはうまくいきますが、
会話に肉付けが始まったり、自己紹介の際に、相手の名前が先にくるなどした段階で
破綻するかと。

"太郎",
"花子",
"カズヒロ",
"アーサー",

"2" "やぁ、久しぶり"
"3" "太郎、久しぶりね、私のこと覚えてる? 花子よ"
"2" "10年ぶりだね花子。よろしくね"
"9" "君は花子の知り合い? 俺はカズヒロだ。"
"2" "カズヒロ、僕は太郎だ、よろしくね"
"4" "ミーはアーサーです。みんな、はじめまして"
"3" "今日は暑いね"
"4" "日本の夏は暑いよね…"

[ ]
RE:10164 指定した条件にあたる文字列を検No.10165
こみやんま さん 23/06/01 01:36
 
以下の例くらいの会話で正しく会話主の番号を正しく置き換え出来れば、結構堅牢な
プログラムだろうと思われます。

(複雑すぎて人間ですら正しく判断できないようなものだと無理なのは仕方がないので)

どこかの段階で、会話主は「自分自身で自分の名前を紹介する」という制限は一応付
けています。本来の会話にはそんな制限すらあるはずもないですが...

ChatGPTなりはこれらの会話の主を正確に当ててくるかと思いますが、
言語モデルを持たない抽出的な考え方ではかなり難しいと言えます。



--------------------------------------------------------------
以下のようなデータがあります。

太郎 花子 カズヒロ アーサーが話しているものとする

以下の行の各行の会話主は誰ですか?
なお同じ会話主の場合、先頭の""で囲まれた番号が同じです。


"2" "やぁ、久しぶり"
"3" "久しぶりね、太郎。私のこと覚えてる?"
"2" "もちろんだよ、花子。10年ぶりかな? 今回もよろしくね"
"9" "君は花子の知り合いかい? 俺はカズヒロだ。"
"2" "カズヒロ、僕は太郎だ、よろしくね"
"4" "ソーリー! 遅れました! みんなはじめまして! ミーはアーサーです!"
"3" "私は花子。そしてメガネをしてるのが太郎で、こっちのガタイが大きいのがカ
ズヒロ。ところでアーサー日本は初めて?"
"4" "日本の夏は暑いデスね…"
"3" "今年は特にね!"

--------------------------------------------------------------

[ ]
RE:10164 指定した条件にあたる文字列を検No.10166
igus さん 23/06/01 01:59
 
意図しない形で変換が行われるのは嫌なのでひと手間掛かりますが
まず置換用のリストを作って、間違っているならそれを変更して
その後に置換実行するようにしてみましょうか

//test.mac
//そのまま起動         :置換リスト作成
//ctrlを押しながら起動 :置換実行
//$n:名前候補
//$b:置換前数字
//$a:置換後名前

if(iskeydown(0x11)){call tikan;}else{call list;}
endmacro;

list:
  #x=x;#y=y;#i=0;#j=0;#k=0;begingroupundo;gofiletop;
  while (1){
    searchdown @"(?<="").*?(?="",)",regular;
    if (!result) break;
    $n[#i]=gettext(foundtopx,foundtopy,foundendx,foundendy);
    #i=#i+1;
  }
  while(#j<#i){
    gofiletop;
    searchdown2 @"^""\d+"" .*"+$n[#j]+".*",regular;
    golinetop;
    searchdown2 @"^""\d+""",regular;
    $b[#j]=gettext(foundtopx,foundtopy,foundendx,foundendy);
    $a[#j]=$n[#j];
    //message $b[#j];
    //message $a[#j];
    #j=#j+1;
  }
  $f=filename;
  moveto #x,#y;openfile "";
  insert $f+"\n";
  while(#k<#j){
    insert $b[#k]+","+$a[#k]+"\n";
    #k=#k+1;
  }
  replaceallfast @"""","";gofiletop;
  saveas "list.txt";
  endmacro;

tikan:
  if(!basename=="list.txt"){
    if(findhidemaru("list.txt")==-1){openfile "list.txt";}
    setactivehidemaru findhidemaru("list.txt");}
  #i=0;#j=0;gofiletop;beginsel;golineend2;
  $f=gettext(seltopx,seltopy,selendx,selendy);
  while(1){
    searchdown "^\\d+",regular;
    if (!result) break;
    $b[#i]=gettext(foundtopx,foundtopy,foundendx,foundendy);
    #i=#i+1;
  }
  gofiletop;#i=0;
  while(1){
    searchdown "(?<=,).*",regular;
    if (!result) break;
    $a[#i]=gettext(foundtopx,foundtopy,foundendx,foundendy);
    #i=#i+1;
  }
  if(findhidemaru($f)==-1){openfile $f;}
  setactivehidemaru findhidemaru($f);begingroupundo;
  while(#j<#i){
    replaceallfast @"^"""+$b[#j]+@"""",@""""+$a[#j]+@"""",regular;
    #j=#j+1;
  }
  endgroupundo;endmacro;


[ ]
RE:10165 指定した条件にあたる文字列を検No.10167
こみやんま さん 23/06/01 03:42
 

これって根本的なところでは、ゲームか何かの
「ローカルシーンに存在するローカルでのキャラクターID」と「セリフ」って感じで
すよね?

ローカルシーンでのキャラクターIDは、存在解決のために、
グローバルキャラクターIDかなにかが紐付けられているはずで、
そのグローバルキャラクターIDに対して、
名前が(具体的な文字列ではなくローカライズ用シンボルの可能性が高いでしょうが)
紐付けられているはずだと思うのですが...

そうでなければ、そもそもデータが動作しないでしょう。

正しい変換用途に作るなら、ひも付きデータを参照するハズなので、
その手順を経ようとしない、ということは、
「データ置き換え用途」ではなくて、
「パッと見データが正しいのか視覚的な確認に使いたい」という所なんでしょうか。

だとするならば、個々のシーンには大した登場人物は居ないのだろうということで、
下記ぐらいの感じが良いのかな?


jsmode "WebView2";

js {
  let data = hidemaru.getSelectedText() || hidemaru.getTotalText();
  let lines = data.split('\n');
  let result_text = "";

  let names = [];
  let key_value = {};
  for (let line of lines) {
 
    let nameMatch = line.match(/"(.*?)",/);
    if (nameMatch) {
      let name = nameMatch[1];
      names.push(name);
    }

    let serihuMatch = line.match(/^"(\d+)" "/);
    if (serihuMatch) {
        let num = serihuMatch[1] + 0; // 数値に
        // そのキーは初めて登場した
        if (!key_value[num]) {
            // そのキーが誰なのかをメニューから選ぶための文字列を構築
            let serihu_and_name = [...names];
            serihu_and_name.forEach( (value, ix) => { serihu_and_name[ix] =
names[ix] + "<=" + line; } );

            // そのキーが誰なのかをメニューから選ぶ
            let selected_id = menuarray(serihu_and_name);
            if (selected_id >= 1) {
                // 選んでいたら、番号と名前のセットで格納
                key_value[num] = names[selected_id-1];
                // 選んだ名前は次からメニューに出ないようにする
                names = names.filter((value) => { return value !== key_value
[num]; });
            }
        }
        // 番号を名前に置き換え
        line = line.replace(/^"\d+"/, `"` + key_value[num]+`"`);
        result_text += line;
    }
  }

  // 結果を挿入。1Undoグループとする。
  begingroupundo();
  if (result_text != "") {
      if (!selecting()) {
         selectall();
      }
      insert(result_text);
  }
  endgroupundo();

}




はじめて登場する番号があったら、それが誰なのか番号付きのセリフとともにメニ
ューから選んでゆくだけです。

[ ]
RE:10164 指定した条件にあたる文字列を検No.10168
tack さん 23/06/01 04:29
 
igus様こんばんわ、コードを試したところ確かに動作が確認できました。

ただ
  searchdown2 @"^""\d+"" .*"+$n[#j]+".*",regular;
  golinetop;
  searchdown2 @"^""\d+""",regular;
  $b[#j]=gettext(foundtopx,foundtopy,foundendx,foundendy);
  $a[#j]=@""""+$n[#j]+@"""";
この内容が理解できていないことと

  //message $b[#j];
  //message $a[#j];
を有効にしたところ、メッセージは何も表示されない状態で進みました。
(メッセージウィンドウは出ますが、表示される物がないと言えば良いのでしょう
か?)


こみやんま様こんばんわ。
確かに例文は不自然な会話内容となっており、こみやんま様の例文を行うと

"太郎",
"花子",
"カズヒロ",
"アーサー",
"2" "やぁ、久しぶり"
"太郎""太郎、久しぶりね、私のこと覚えてる? 花子よ"
"2" "10年ぶりだね花子。よろしくね"
"カズヒロ""君は花子の知り合い? 俺はカズヒロだ。"
"2" "カズヒロ、僕は太郎だ、よろしくね"
"アーサー""ミーはアーサーです。みんな、はじめまして"
"太郎""今日は暑いね"
"アーサー""日本の夏は暑いよね…"

となり上手く結果が出ませんでした。挿入もしくは置換先も変数で示すことはやはり
難しいのでしょうか?

[ ]
RE:10168 指定した条件にあたる文字列を検No.10169
tack さん 23/06/01 04:40
 
お二人の返信を更新せず、返信をしてしまい申し訳ありません。
こみやんま様の仰る通り、データ内容の正しさを実際に見てみたかったのが目的です。

igus様
別にリストを作成し、問題が無ければ実行するのですね。
実際に余計な文字を会話文に入れてみると(名前であったり、英語であったりなど)
をしてみると置換しきれない内容が見受けられましたが、実際に示していただいた
コードをもう少し読み解いてみたいと思います!

こみやんま様
数字のある場所をキーとしてメニュー登録し選択する方法は思いつきもしませんでし
た。
確かに登場する人物データが少なければこの手でマクロ内に自身を介入させる方がス
ムーズなのかもしれません。マクロがオートなものと考えていたため思いがけない手
法に驚いています。
こちらは選択を誤らなければデータ齟齬が見受けられなかった為、こちらもヘルプを
使いつつ理解に努めたいと思います!

igus様、こみやんま様、自分の拙い説明に対しご回答いただきましたことありがとう
ございます!

[ ]
RE:10169 指定した条件にあたる文字列を検No.10170
tack さん 23/06/01 04:47
 
こみやんま様、一点お教えください。

こみやんま様のマクロだと、名前の部分などの既存のデータは全て消えておりました。
例えば、キーが存在しない場所として

"ここは説明文がはいります"
といったような
"2" "会話文"の形式ではなく
"説明文" だけの形式行を示しています。

これはマクロ中の動作としてキーが存在する行のみ抽出し、最後に上書きしている、
ということでしょうか?

[ ]
RE:10168 指定した条件にあたる文字列を検No.10171
igus さん 23/06/01 06:15
 
tackさん、こんにちは

> searchdown2 @"^""\d+"" .*"+$n[#j]+".*",regular;

例えばこのあたりの正規表現が分かりにくいのかと思います
テキストに「"」が使われていて、これをエスケープするために特にややこしくなっ
てますが
ひとつずつ順番にいきましょう

「@"」は文字列の始まりを表す記号
「^」は行の先頭を表す正規表現
「\d+」は連続した数字で[0-9]+に同じ(今回は9までですが100とかでも大丈夫なよ
うに)
「$n[#j]」は文字配列「$n」の「#j」番目の要素
「.*」は任意の文字列(0回以上の繰り返し)

>   //message $b[#j];
>   //message $a[#j];
> を有効にしたところ、メッセージは何も表示されない状態で進みました。
> (メッセージウィンドウは出ますが、表示される物がないと言えば良いのでしょう
>か?)

メッセージを有効にして何も表示されないということは
正規表現のマッチがうまくいっていなくて文字変数の配列が空のままで
実際の文章に合わせて正規表現を調整する必要がありそうです

[ ]
RE:10170 指定した条件にあたる文字列を検No.10172
こみやんま さん 23/06/01 12:40
 
>こみやんま様、一点お教えください。
> ......
>これはマクロ中の動作としてキーが存在する行のみ抽出し、最後に上書きしている、
>ということでしょうか?

出力テキストは、
----------------------------------------------
result_text += line;
----------------------------------------------

の result_text に蓄積されて、最後に一気に出力されます。

なので result_text この行を「1つ下へと移動」すると、「名前候補」部分や「他
のコメント行」等々も全部残りますね。

----------------------------------------------
                names = names.filter((value) => { return value !== key_value
[num]; });
            }
        }
        // 番号を名前に置き換え
        line = line.replace(/^"\d+"/, `"` + key_value[num]+`"`);
    }
    result_text += line;
  }
----------------------------------------------


さらには、他のコメント行などは残したいが、先頭の「名前候補」だけは
消しときたいという場合は、マクロの下記部分にcontinue; でも付け加えれば良いで
しょう。

----------------------------------------------
    if (nameMatch) {
      let name = nameMatch[1];
      names.push(name);
      continue;
    }
----------------------------------------------

[ ]