マクロ 「昇順で並べ替え」について

  1. マクロ 「昇順で並べ替え」を実行すると先頭行が空行になって、最終行の改行がなくなりますがいいんですかね。
    元のテキスト
    2
    1
    3
    [EOF]
    マクロ実行

    1
    2
    3[EOF]
    期待する結果
    1
    2
    3
    [EOF]

     |  foo  |  返信
  2. 元のテキスト
    3
    2
    1[EOF]
     マクロ実行 だと期待する結果

    2
    3[EOF]
    になるのかな

    最後の改行群まとめてどけておいて処理してから最後に戻せばよさそうだ

     |  クリ廃止  |  返信
  3. ● foo さん

    これは javascript の sort が空文字を一番小さいとみなす仕様のためです。
    昇順なら、10 行目の sort() を以下のコードにすると想定された動きになります。
    sort(function(a,b){return !a ? 1 : (!b ? -1 : (a > b ? 1 : -1))})

    このような形で、(マクロ内埋め込みですが)ソート方法を色々とカスタマイズできます。
    (大文字小文字の区別有無とか、文字じゃなくて数値で比較するとか、CSV の N 列で比較するとか……)

     |  ks  |  返信
  4. みなさんのご指摘の通りですね。

    同梱のマクロはあくまでサンプルマクロです。なるべく複雑な仕様にせず、マクロファイルを開いた人が「お、こんな簡単に書けるのか!」と興味を持っていただくことを狙っていますので、ちょっとご希望の動作と違うかもしれませんが、ご容赦いただければと思います。

    ks さんがおっしゃる通り、JavaScript の sort をカスタマイズすればどんなソートでもできます。

    サンプルマクロとしては、「昇順で並べ替え」で基礎編、「降順で並べ替え」でソート方法のカスタマイズのサンプルになっていますので、ソート方法をカスタマイズしてお気に入りの動作を作ってみるのも面白いですよ^^

     |  Kuro  |  返信
  5. ksさん
    いや、空行を最後にしたいわけではないです。
    元々のテキストには空行が含まれていないのにソート後に空行があるのは仕様としてどうなんだろうを思っただけです。
    実際にこのマクロを使ってるわけではないです。
    http://www.haijin-boys.com/23.htmlの方にも書いたけど、秀○の変換モジュールをMeryから使うためのプラグインを書いたのでもっぱらそっちを利用しています。(変換モジュールも自作のものです)

     |  foo  |  返信
  6. ● foo さん

    1 行目「2」、2 行目「1」、3 行目「3]、4 行目「」の全 4 行で、この 4 行目の空行が先頭に来るのが問題なのかと思っていました……
    最終行は EOF のみなのだから行ではなくソート対象にもならない、というのは流儀に依りけりかと。
    正規表現の行末「$」を EOF にマッチングさせるのか、といった仕様の狭間と同じなのかなーと思います。
    (愚直な解釈だと EOF は行末ではないのかもしれませんが、利便性考えると引っかかったほうが良いとか……)
    このあたり、秀○だとそういった動きになるのでしょうか。

    http://www.haijin-boys.com/23.htmlの方にも書いたけど、秀○の変換モジュールをMeryから使うためのプラグインを書いたのでもっぱらそっちを利用しています。(変換モジュールも自作のものです)

    この変換モジュールはすげー、と思います。
    秀○は歴史とそれに付随する資産が大きいですから、使えるシーンはかなり多いのではないでしょうか。
    ただ Kuro さんが懸念されているように、マクロ作者の秀○愛をどう受け止めるのか、という点は公開されるようでしたら留意が必要かもしれません。

     |  ks  |  返信
  7. > 1 行目「2」、2 行目「1」、3 行目「3]、4 行目「」の全 4 行で、この 4 行目の空行が先頭に来るのが問題なのかと思っていました……
    > 最終行は EOF のみなのだから行ではなくソート対象にもならない、というのは流儀に依りけりかと。
    う~ん、どうなんだろ。
    たとえば
    こんなテキストで
    a
    3
    2
    1
    a
    aに囲まれた数字だけの行を選択してソートしたいとすると、今の仕様だと最終行の改行は選択せずにマクロを呼び出さなければならないけど、私の感覚だと最終行の改行も含めて選択する方が自然な感じがするんですが。

     |  foo  |  返信
  8. 他のエディタのソートの仕様も確認したところ、10個以上のエディタを試してみましたが、Meryと同じく、最終行が改行無しの空行があると解釈しているエディタはNotepad++だけでした。

     |  foo  |  返信
  9. ● foo さん
    > aに囲まれた数字だけの行を選択してソートしたいとすると、今の仕様だと最終行の改行は選択せずにマクロを呼び出さなければならないけど、私の感覚だと最終行の改行も含めて選択する方が自然な感じがするんですが。

    選択、という観点が抜けていました。
    確かにその動きの中では、おっしゃるような動きの方が自然、という意見は大いにあるかと思います。

    ただ結局このソート自体はネイティブではなくマクロなので、Kuro さんのおっしゃるように変更すればどうとでもなってしまいます。
    全選択時に Selection.Text と Document.Text が同じ結果を返す現状は正と思いますので、マクロ自体は想定した動きをしていると思います。
    それ以上はマクロ作者の理念と頑張り次第(改行終端ならそれを除く処理の追加)でしょうか。
    (個人的にはそうは思いませんが)不親切な UI をサンプルとして提供するのはいかがなものか、というのはごもっともですが。

    ただ探ってみると、テキストをどう捉えるか、という問題に行き着きました……
    > 改行を「行と行の separator」と考えるかそれとも「行の terminator」と考えるかはシステムによって分かれるが、
    > 一般には POSIX のように terminator と考えるのでファイルの最後には改行が必要だという理屈。
    http://hiroakiuno.hatenablog.com/entry/20070409/p1
    確かに最後が改行で終わっていないと都合が悪いソフトって、意外と散見されますね。
    ラインエディタとか歴史的な経緯なんかも絡んできそう……
    根が深いですね。

     |  ks  |  返信
  10. foo さん、ks さん、Kuro さん
    興味深いお話をありがとうございます。

    これは foo さんのご指摘に賛同します。

    皆さんのように、素早く思い通りにマクロやプラグインを
    作ることができる方にとっては、これは作る人に向けた小さな
    キッカケだけだから、機能が足らないのは仕方ないんだよ、
    というのは自然なお考えです。

    でも、添付されている「標準のツール」としてこのマクロをお使い
    になる人もいることも視野に入れていただけるのはありがたいです。

    そのコードが小さくても、わずかに工夫すれば実用品としてより便利
    になるのなら、そうなっていたら、ちょっとうれしい。

    深い技術論もぜひ聞きたいのですが、脇で聞き耳をたてている者
    としては、願わくば、具体的に動くコードやデータのやり取りで
    対話をしていただけると、何を話しているのか試してわかり易いし、
    テクニックも手に入って一石二鳥なので (笑)

    とエラそうなことを言った手前……、皆さんには当たり前過ぎるの
    かも…、でも実践してみます。

    Mery についてのお願いをするときには、世の中のエディタでは…、
    という表現は、なかなか効果がある手ですけど…、

    これは sort の話というよりは、テキストを split("\n") で配列
    にしてからゴニョゴニョして join("\n") でテキストに戻す、という
    流れについてまわる、最後の行終端のバラつき、の話なのでは。

    元通りに復元するのでなく、必ず行末は改行で終わるもの、として
    なければ付加してしまうのなら、

    // 昇順
    document.selection.Text = document.selection.Text.replace(/\n$/,"").split("\n").sort().join("\n")+"\n";

    // 降順
    document.selection.Text = document.selection.Text.replace(/\n$/,"").split("\n").sort(function(a, b){ return ((a < b) ? 1 : ((a > b) ? -1 : 0)) }).join("\n")+"\n";

    のように置き換えると、

    元のテキストA
    2
    1
    3
    [EOF]
    マクロ実行

    元のテキストB
    2
    1
    3[EOF]
    マクロ実行


    ※結果はAとBとも同じ※
    ※昇順※
    1
    2
    3
    [EOF]

    ※降順※
    3
    2
    1
    [EOF]

    となりますけど、ダメですかね…

     |  inuuik  |  返信
  11. 皆様のご指摘の通りですね、機能不足なものを提供してしまい申し訳ございませんでした。
    実用的なコードを頂けると大変参考になりますね^^

    > // 昇順
    > document.selection.Text = document.selection.Text.replace(/\n$/,"").split("\n").sort().join("\n")+"\n";

    これでご希望の動作はほぼ OK だと思いますが、最後の改行を選択範囲に含めなかった場合に余計な改行が付加されてしまいますね。

    ちょっと長くなってしまいそうなので with を使ってごまかしていますがこんなのどうでしょうか?

    // 昇順
    with (document.selection)
        Text = Text.replace(/\n$/, "").split("\n").sort().join("\n") + (Text.charAt(Text.length - 1) == "\n" ? "\n" : "");

    選択範囲の最後に改行がある場合のみ "\n" を付加するようにしただけですが…
    力技っぽいのでもう少しスマートに決まると美しいですが^^;

     |  Kuro  |  返信
  12. > もう少しスマートに
    document.selection.Text = document.selection.Text.replace(/\n$/,"").split("\n").sort().join("\n") + (RegExp.lastMatch);

    マクロファイルを eval で実行する別マクロを使っている場合、冗長に
    ((RegExp.lastMatch=="\n")?"\n":"")
    とすれば ok

     |  ちょっと匿名で  |  返信
  13. > document.selection.Text = document.selection.Text.replace(/\n$/,"").split("\n").sort().join("\n") + (RegExp.lastMatch);

    おぉ、コレは素晴らしいですね!
    lastMatch って直前に一致したヤツを覚えてくれているのですね。

    > マクロファイルを eval で実行する別マクロを使っている場合、冗長に
    > ((RegExp.lastMatch=="\n")?"\n":"")
    > とすれば ok

    マクロファイルを eval から実行した場合、なぜ最初のコードだと駄目なのかわかりません…^^;

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

    > 最後の改行を選択範囲に含めなかった場合に余計な改行が付加されてしまいますね。

    やはりキッチリ詰めるのね(^^;

    そしてスマート版、いいです!

    eval でマクロ起動してますが、ちゃんと動きました…?

    このマクロだけなら問題ないのですが、この文を他のマクロに使うときには
    ちと注意が

    RegExp はグローバルオブジェクトなので、同じマクロ内の、前にある正規表見
    の結果の影響を受けます。

    初期値ではこの /\n$/  のマッチに失敗すると lastMatch に "" が返ります
    が、もし前に正規表現があってマッチ成功していると、その結果をそのまま
    返します。

    なので、最後に改行をつけないとき、予想外の内容を最後に付加してしまう。

    これは /\n?$/ にして、いつもマッチさせれば回避。

    document.selection.Text = document.selection.Text.replace(/\n?$/,"").split("\n").sort().join("\n")+RegExp.lastMatch;

    Kuro さん作なら /\n$/ のままで、ちと短縮。

    with (document.selection)
      Text = Text.replace(/\n$/, "").split("\n").sort().join("\n")+(Text.slice(-1)=="\n"?"\n":"");

     |  INUUIK  |  返信
  15. ご返信ありがとうございます。

    おぉ、完璧ですねコレ!
    なるほど、lastMatch はマッチしなかった場合は前の結果が残ったままになるのですね、勉強になりました。

    ----
    // 昇順
    document.selection.Text = document.selection.Text.replace(/\n?$/,"").split("\n").sort().join("\n")+RegExp.lastMatch;

    // 降順
    document.selection.Text = document.selection.Text.replace(/\n?$/,"").split("\n").sort(function(a, b){ return ((a < b) ? 1 : ((a > b) ? -1 : 0)) }).join("\n")+RegExp.lastMatch;
    ----

    標準で同梱されている昇順・降順マクロを↑ このコードに差し替えたく思いますが、著作権の表示が必要な場合はご返信いただけますでしょうか。(特になければ更新履歴にスペシャルサンクス表記とさせていただきます)

     |  Kuro  |  返信