「hogehoge」を「/^hoge//」で置換するとすべてのhogeが消えてしまう。

  1. いつもお世話になっております。
    宇宙一のテキストエディタMeryを愛用させていただいております。
    以前は質問(https://www.haijin-boys.com/discussions/5034)へのご回答をありがとうございました。
    今回は、正規表現を使用した置換で気になる動作がありましたので報告させていただきます。

    ■概要

    「hogehoge」だけが記述された行を「/^hoge//」で置換すると、行頭のhogeだけが消えてほしいが、すべてのhogeが消えてしまう。

    ■再現手順

    (1) Mery(最新版 Mery-x64-2.6.7.zip あるいは最新ベータ版 Mery-x64-3.0.4.zip)をダウンロードする。

    (2) 解凍したあとにMery.exeを起動して、下記の【】内のテキストをMeryにコピペする。
    【hogehoge】

    (3) 検索メニューから置換を選び、置換ダイアログを表示させて「正規表現を使用する」だけをチェックする。

    (4) 置換ダイアログの文字列入力欄を下記の【】内のテキストにして「すべて置換」を押す。
    ・検索する文字列【^hoge】
    ・置換後の文字列【】(何も入力しない、つまり行頭のhogeのみを削除する指示)

    ■結果

    すべてのhogeが消える。

    ■期待する結果

    行頭のhogeだけが消えて、次のhogeは残るはず。

    ■所感

    「置換後の文字列」入力欄に何か入力した場合、例えば【fuga】を入力した場合の結果は【fugahoge】となり、期待通りの結果でした。

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

     |  すーた  |  返信
  2. 横から失礼します。

    > 行頭のhogeだけが消えてほしいが、すべてのhogeが消えてしまう。

    Javascriptマクロなら以下のように gm フラグを使った1行のコードで済むのですが、Mery本体の置換機能で行頭の文字列について「すべて置換」を行うとそうなっちゃうんですよね…。

    document.Text = document.Text.replace(/^hoge/gm, "");

    以前フォーラム内の別のトピックで教えていただいた方法ですが、置換ダイアログで以下のように指定すると「1行につき各1回だけの行頭文字列の置換処理」ができます。 :)
    ref. https://www.haijin-boys.com/discussions/4395#discussion-4417

    検索する文字列: ^(?:hoge)(.*+)$
    置換後の文字列: $1
    ☑正規表現
    ⇒「すべて置換」

     |  sukemaru  |  返信
  3. >> すーた さん

    Mery をご愛用いただきありがとうございます。

    > 「hogehoge」だけが記述された行を「/^hoge//」で置換すると、行頭のhogeだけが消えてほしいが、すべてのhogeが消えてしまう。

    [すべて置換] の仕様は [置換] を繰り返す方式と、文書全体に対して 1 度だけ置換する方式がありまして、Mery の [すべて置換] は [置換] を繰り返す方式を採用しているため、こういう結果になります。

    > ・検索する文字列【^hoge】
    > ・置換後の文字列【】(何も入力しない、つまり行頭のhogeのみを削除する指示)

    ↑ この条件で [すべて置換] ではなく [置換] のボタンを連打していただくと、何が起きているか分かりやすいと思います。

    Mery と同様の方式のものは、例えば世界一 (現在は世界三?) のテキストエディター Notepad++ や、宇宙一 (価格的にw) の開発ツール Delphi などがあります。

    ちなみに今回のケースですと、置換した後にカーソル位置が先頭に残らないように正規表現をちょっと工夫すれば回避できます。

    検索文字列:^hoge(.*)$
    置換文字列:\1

    Mery の仕様を把握していて正規表現にも詳しいかたからはこの方式のほうが細かいことができて取りこぼしがないからイイネというご意見をいただくこともありますが、国産のテキストエディターですとこの方式は少ないですし、直観的じゃないので戸惑ってしまいますよね。

     |  Kuro  |  返信
  4. Kuro様
    sukemaru様

    早速のご回答ありがとうございました。助かりました。

    「すべて置換」ボタンと「置換」ボタンの結果の違いを、たんに「途中確認なしで一気に置換実行」と「都度マッチ箇所を確認しながら置換実行」だと勝手に思い込んでいましたが、 実際はそうではないとのことで、Meryにおける正規表現の仕様/実装を理解しました。

    対処法も示していただきありがとうございました。大変参考になりました。

    ちなみに今回の正規表現の件について個人的にちょっと気になったので下記のページにまとめました。
    https://sites.google.com/view/yawaraka-labo/RegexInMery

    P.S.
    これまでたくさんのテキストエディタ(古いものだとMifes/vi/Emacsや新しいものだと窓の杜で紹介されているすべてのテキストエディタ)を使ってきましたが、どれも一長一短で(とくに盛り込みすぎの機能やUIは苦手で)、個人的には宇宙一使いやすいエディタは「Mery」と「vi」です。Meryの良さは必要十分な高機能と分かりやすさとサッパリしたUIと安定性にあると思っています。ツールバーのアイコン大きさを変えられるのも個人的には最高です(タブレットで使うこともあるのでボタンは大きいほうがいい)。素晴らしいソフトウェアをありがとうございます。🤗

    それと、もし差し支えなければですが、開発支援として少ない額ではありますがAmazonギフト券をご進呈させていただければと思っております。Kuro様のメールアドレス(Amazonアカウントのメールと同じである必要はなく捨てメールアドレスでもOK)さえ教えていただければ、お互いに個人情報一切を伏せたままでAmazonギフト券の番号をEメールでお知らせすることができます。もし、Kuro様さえよろしければですが(かつAmazonをご利用でしたらですが)、下記のページからメールアドレスを教えていただければと思います。※もちろん、個人間のやりとりは心配だと思いますので、その場合は本件無視してください。🤗
    https://sites.google.com/view/yawaraka-labo/Contact

    それでは失礼いたします。

     |  すーた  |  返信
  5. 基本的に ^hoge での検索・置換に慣れているということでしたら、Mery 3.0.0以降のマルチカーソル機能を有効にしたうえで [検索] ダイアログを開いて以下のように指定すると、削除したい部分である「行頭のhogeだけ」がすべて(複数)選択された状態になります。

    検索する文字列: ^hoge
    ☑正規表現
    ⇒「すべて検索」

    タイトルバーまたはステータスバーあたりをクリックするか Esc キーで検索ダイアログを閉じるかしてエディタ領域をアクティブにしてから Delete または Backspace すれば、「行頭のhogeだけが消えて、次のhogeは残る」という結果が得られますね。 :)

     |  sukemaru  |  返信
  6. sukemaru様、追加の情報ありがとうございます。助かります。

    Kuro様、いま、こちらのページに気づきました。
     ↓
    寄付・開発支援について
    https://www.haijin-boys.com/software/donate/donate

    さっそくAmazonほしい物リストにあったとある商品をポチらせていただきました。
    購入時に「ギフトの設定」チェックを外して、支払いをAmazonギフト券にすれば
    匿名で手続きできるらしいので、そのようにしました || したつもりです。

    ところで、上記ページをはじめて読ませていただいたのですが、MeryはDelphiで開発されていらっしゃるのですね。
    Delphiというとたしかボーランドですよね?
    ボーランドといえば・・・つい先日(!)のことだったと記憶していますが、Trubo C(!)というコンパイラで言語の独習をしていたことを思い出します・・・
    たしか当時は黄色いパッケージで売られていて、分厚い辞書のようなマニュアルが付属していました・・・
    たしかドスだかチャカだかの名前の某OS上にて、キカイ(1MB RAM)にペラペラのシートのようなブッタイを差してソフトウェアを起動し、コマンドライン上でコンパイルやリンクしていたのを思い出します・・・
    しばらくして気が付いたらDelphiというPascalベースの新しい言語/環境をボーランドが開発していました・・・
    最近のことなのに妙に懐かしいですw

    それでは失礼いたします。

     |  すーた  |  返信
  7. ご返信ありがとうございます。

    すてきな実験室ですね、興味深く拝見させていただきました。かゆいところに手が届く感じのアプリで、ネーミングセンスが光ってますね😉

    説明不足だったようで、ちょっと誤解があるようですから補足ということで説明させていただきたいと思います。

    > 「すべて置換」ボタンと「置換」ボタンの結果の違いを、たんに「途中確認なしで一気に置換実行」と「都度マッチ箇所を確認しながら置換実行」だと勝手に思い込んでいましたが、 実際はそうではないとのことで、Meryにおける正規表現の仕様/実装を理解しました。

    ↑ これは逆で、Mery の仕様は "たんに「途中確認なしで一気に置換実行」と「都度マッチ箇所を確認しながら置換実行」" です。

    Mery の [すべて置換] は正規表現の /g フラグ (グローバルマッチ) ではなく、文書の最後に到達するまで [置換] を繰り返すものとなっています。

    通常、ユーザーさんはすべて置換したいときに、何度か [置換] ボタンを試して、それが機能しているかを確認してから [すべて置換] ボタンを押しますよね。

    もし、そこで [置換] と [すべて置換] の挙動が変わってしまうと、多くのユーザーさんは困ってしまうと思います。

    Mery のユーザーさんには正規表現を知らないかたも多いので、[すべて置換] は [置換] の繰り返しというシンプルな仕様は初心者さんには分かりやすく、また正規表現に詳しいかたにとっては十分な仕様というかたちになっています。

    今回、例としてご提示いただいたサンプルですと hoge がすべて消えてしまったというインパクトが強いため「あれ?」と思ってしまうかもしれませんが、例えば以下の例。

    hogehogeaaaa
    hogehogebbbb
    hogehogecccc

    といったテキストがあったとして、

    検索する文字列: 「^hoge」
    置換後の文字列: 「」 ←空っぽ

    で、ユーザーさんが [置換] ボタンをクリックしたとします。すると、まず、最初にマッチした行頭の hoge が一つ消えます。

    hogeaaaa
    hogehogebbbb
    hogehogecccc

    ユーザーさんは、続けてもう一度 [置換] ボタンをクリックします。当然、もう一度、行頭の hoge が一つ消えます。

    aaaa
    hogehogebbbb
    hogehogecccc

    さらにもう一度 [置換] ボタンをクリックするとこう。

    aaaa
    hogebbbb
    hogehogecccc

    ここまでは、Mery を含め、どのエディターでもそうなっていると思います。

    さて、ここからですが、一般的なユーザーさんはこのまま [置換] を続ければ行頭の hoge をすべて削除できると思うはずですから、[すべて置換] ボタンを押してすべて置換しますよね。

    aaaa
    bbbb
    cccc

    結果、期待したとおりに先頭の hoge をすべて削除できるわけです。

    正規表現に詳しいかたからすると、グローバルマッチという概念を知っていますから「^hoge」は行頭の hoge を 1 つだけ削除するものだと思ってしまうかもしれません。

    しかしながら、すべてのユーザーさんがプログラマーだったり正規表現に詳しかったりするわけではありませんから、[置換] と [すべて置換] の結果を一致させるという仕様は、一般的なユーザーさんにとっては分かりやすいと思います。

    P.S.
    Mifes vi Emacs、歴史のあるエディターばかりでそこに並ぶのは非常に気が引けますが、vi が最強には同意です。

    > ところで、上記ページをはじめて読ませていただいたのですが、MeryはDelphiで開発されていらっしゃるのですね。

    はい、Delphi で作っています。私も、つい先日 (笑) のボーランド時代から使っています。

    当時のプログラミング言語は立派な箱に入っていて、電話帳ぐらいのマニュアルが付いてましたよね。あの高級感、そして毎日コツコツとマニュアルを読む楽しさ。

    残念ながら私の時代にはすでに RAM が 8MB ぐらいになっており、ドスの上で Windows 3.1 が動いていました。Delphi を知ったのはその頃でしたが、VB と Delphi で迷って、結局 VB を買ってしまったことを今でも後悔しています。

    開発支援のご協力ありがとうございます。ほしいものリストの仕組みは良く分かっていないのですが、知り合いのススメで始めてみました。

    Amazon で注文した覚えのないものが突然やってくるのでめちゃくちゃビックリしますが、それと同時にめちゃくちゃ嬉しい気持ちになります。

    頂いたご支援は開発費、開発環境の更新費、素材やコンポーネント、参考書籍の購入費、ブログサイトや Wiki サイトの維持費として大切に使わせていただきたいと思います😆

     |  Kuro  |  返信
  8. Kuro様

    詳細なご説明ありがとうございました。いろいろな方が使用することを想定しての今の実装となっていることを理解しました。

    私の場合、一般的な正規表現で一括置換という作業がこれまでの人生で普通だったため、今回のことでびっくりしてしまいましたが、もともと正規表現といっても処理系やアプリで異なることがありますよね。(バグ含む)(そもそもビル・ジョイ作のunix版vi/sed/awk等にもバグはある笑)。最終的にはそれぞれのソフトウェア作者さんのポリシー・思想・現実などの結果がアプリ実装となることは当然のことだと思います。

    開発支援の額が少なすぎてお恥ずかしい限りですが、現在生活に余力のない状況のためご容赦ください~

    このたびは自分自身もいろいろ勉強になりました。あらためてありがとうございました!

     |  すーた  |  返信
  9. ちょっと気になったので、あれから他のテキストエディタ(窓の杜にあったやつ)で今回の正規表現の置換について調べてみました。参考までに調査結果を記しておきます。

    ■ EmEditor Professional 32-bit Version 20.2.2 (最新)
    ■ 秀丸エディタ Version 8.95 32-bit (最新)
    ■ Notepad! Version 1.0.16.56 64-bit (最新)
    ■ gPad ver 3.0.2 (最新)
    ■ MARKDOWN EDIT Version 1.35 (最新)
    ■ vim version 8.2 (最新)
    ■ K2Editor r.1.5.9 Build 522 (最新)
    ■ xyzzy version 0.2.2.253 (最新)
    □ Notepad++ v7.9.1 (最新)
    ■ ViVi version 3.08.045 (最新)
    ■ Microsoft Visual Studio 2019 (最新)

    ■印のエディタはすべて同じ結果となっており、私が期待したとおりの動作/結果でした。

    □印のエディタは他のエディタとは異なり、Meryと同じ結果となりました。Notepad++は他にもいろいろ試してみましたが動作が独特で、Regex実装の明確なルールを把握できませんでした・・・

    調査結果のエビデンス(スクショ)は下記の場所に格納しました。
    https://drive.google.com/drive/folders/1YBJAQWQH_0mHGY_ZMulVmT-jKHFJiLdh

    ※なお、今回の件はテキストの例として「hogehoge」をあげましたが、じつは私がMeryでやった作業は別の文字列でして、「行頭のタブの最初の1個だけを削除してインデントを下げる」ということをやろうとしました。具体的には「/^\t//」の置換になります。なぜこのような置換をしたかというと、ソースコードにせよ日本語文書にせよ、タブで段付け(インデント)して読みやすくするのはよくやると思うのですが、文書構造が深くなるとインデントも深くなり、行頭のタブの数も多くなります。で、ある個所の文章を別の箇所にコピペする際にコピー先の部分とインデントが異なることがよくあるため、コピペしたテキスト群のインデント(行頭のタブ)を何個か削除(あるいは追加)するという作業を正規表現でよくやります(あくまで私の場合です)。で、こういった置換は普段viでやることが多いのですが、たまたまMeryでやってみたところ「行頭のタブが全部消えてしまった」となったわけです。つまり正規表現的に「/^\t\t*//」と同じ結果となり、気になったので念のためご報告させていただいた次第です。

    というわけで、長々と失礼いたしました。今回あらためて他のエディタを自PC(Windows10 64-bit)にインストール or 解凍して動作やUI等を見てみましたが、やはり私はMeryが良いなとあらためて感じました。なんといいますかアプリ全体にスッキリした思想が統一されているといいますか、盛り過ぎず少なすぎず、マーカーやスニペットやマクロ等の機能が充実しているのに複雑さを感じないといいますか(アイコン群もかわいくて好きです)・・・最高のテキストエディタをありがとうございます!

     |  すーた  |  返信
  10. ご返信ありがとうございます。

    そうですよね。この仕様は Unix を使うかたからすると違和感があるようで、以前にも sed と違うということで同様のご質問をいただいたことがあります。
    https://www.haijin-boys.com/discussions/3297

    > Notepad++は他にもいろいろ試してみましたが動作が独特で、Regex実装の明確なルールを把握できませんでした・・・

    お役に立てる情報かどうかわかりませんが、Notepad++ は FAQ に正規表現の実装について書かれていたと思います。
    https://notepad-plus-plus.org/community/topic/15765/faq-desk-where-to-find-regex-documentation

    面白そうなので私も手元の環境に入ってるエディターや、Wikipedia のテキストエディター一覧の中から聞いたことのある名前のエディターを使って同じ条件で検証してみました。

    ■ Atom 1.52.0
    ■ Brackets 1.14
    □ Delphi 10.3
    □ eclipse 4.17.0
    □ Lazarus 2.0.10
    □ MIFES 10.04
    □ GNU nano 2.2.6
    ■ GNU nano 5.3
    □ NetBeans 12.1
    □ Notepad++ 7.9.1
    □ QXエディタ a0.80
    □ SciTE 4.4.5
    ■ Sublime Text 3.2.2
    □ UltraEdit 27.10.0.148
    □ UnEditor r.1.10.4 (NoEditor r.1.33.3)
    □ WinMerge 2.16.8.0
    ■ WZ EDITOR 10.0.13tr
    ■ サクラエディタ 2.4.1.2849
    □ 真魚 2.31.5.0
    □ Mery 3.2.0

    Linux で試してみたのは GNU nano ですが、2.2.6 は □ でしたが、最新の 5.3 を入れてみたら ■ に変更されていました。

    唯一、UltraEdit はデフォルトですと □ ですが、正規表現の設定で Unix 互換モードにすると ■ に切り替えることができました。ということは、■ の仕様って Unix 風なのかも?

    本件につきましては、FAQ コーナーかどこかに情報を記載しておこうと思います。

    > 行頭のタブの最初の1個だけを削除してインデントを下げる

    なるほど、そういうシーンはありますね。

    Mery ですと [編集] の [選択範囲の変換] の中に [逆インデント] という機能がありますので、インデントを下げたい部分を選択してこの機能を使えば簡単かと思います。

    キーボードで操作する場合は範囲を選択して Shift + Tab で [逆インデント]、Tab で [インデント] となっています。

    > 今回あらためて他のエディタを自PC(Windows10 64-bit)にインストール or 解凍して動作やUI等を見てみましたが、やはり私はMeryが良いなとあらためて感じました。

    ありがとうございます。

    最近、少々、エディター開発のモチベーションが低下していましたが、こうして他のエディターに触れてみると刺激になって良いですね。

     |  Kuro  |  返信
  11. ご返信ありがとうございます。

    テキストエディタって国内/海外/メジャー/マイナーと沢山あるんですね。 日本の一般的なWindowsユーザーだと窓の杜(第三者評価系)で入手する方が多いのではと想像してます。※他サイトはピンキリな玉石混合系や信頼無担保系なのもチラホラ。

    というわけで、エディタの置換における正規表現の細かい差異に関しては、最終的には使う側の慣れや好みですね。今回、いろいろ把握できてよかったです。🤗

     |  すーた  |  返信
  12. 余談です。

    いまさらながら気が付いたのですが、Meryには便利なマクロ機能が実装されているので、それぞれのユーザーが必要に応じて好みの置換機能を作ってしまえばいいんですよね。😅

    そのヒントをじつはsukemaru様が最初の返信で提示(暗示?)されていたように思えます。ありがとうございます。

    超テキトーですが例えば、

    var s = prompt("search for (e.g. \"^hoge\" \"^\\t\\t*\") : ", "");
    if (s == "") Quit();
    var f = prompt("regex flags (e.g. \"g\" \"gm\" \"gmi\") : ", "");
    var r = prompt("replace with : ", "");
    if (document.selection.Text == "") { document.selection.SelectAll(); }
    document.selection.Text = document.selection.Text.replace(new RegExp(s, f), r);

    で、このマクロをショートカット(CTRL-F)に割り当てる云々。

    # 以下はテキトーに書いてます。自分はマクロに関してウワベだけかじってるニワカです。

    さらに、メニューやツールバーアイコンからの置換ダイアログ表示をフック(できるのかな?)してマクロのほうを実行させたり、あるいはマクロからの置換ダイアログAPI(あるのかな?)呼出し時にフラグ(今回の例で言えば■と□の機能の違いを選べるようなフラグ?)を渡せばアッサリ済んでしまうのかもしれませんね・・・

     |  すーた  |  返信
スポンサーリンク