[ 新規に投稿する ]

空白行区切りテキストをタブ区切りテキストに変換するNo.08712
kuke さん 18/07/03 08:43 [ コメントを投稿する ]
  空白行区切りテキストが、あります。
このテキストを表に変化するために、列の数(2)を与えて、タブ区切りのテキストに反抗したいと考えています。

テキスト上部には、任意の数の空白行が存在する可能性がありますが、これは、タブ区切りのテキストには反映させません。

複数の空白行の連続は、1つの区切りと判断します。

○加工前のテキストの例
********************************************

メンバー

解説

CompletionListBox()

CompletionListBoxクラスの新しいインスタンスを初期化します。

ActualHeight

この要素のレンダリングされた高さを取得します。
(FrameworkElementから継承されています)

ActualWidth

この要素のレンダリングされた幅を取得します。
(FrameworkElementから継承されています)
********************************************

○期待する加工後のテキスト

tab記号は、[tab]で表しています。
********************************************
メンバー[tab]解説
CompletionListBox()[tab]CompletionListBoxクラスの新しいインスタンスを初期化します。
ActualHeight[tab]この要素のレンダリングされた高さを取得します。(FrameworkElementから継承されています)
ActualWidth[tab]この要素のレンダリングされた幅を取得します。(FrameworkElementから継承されています)
********************************************

マクロを作成しました。しかし、途中までは、うまく動作しているようですが、ループの終了条件が間違っているようで、最終的には、意図したテキストになりません。マクロも終了しません。

○作成したマクロ
********************************************

/// 空白行区切りテキストをタブ区切りテキストに変換する
/// 空白行区切り→タブ区切り

/// 加工前の区切りは、空白行
/// 2列の表に変更する。1行目は、項目名

gofiletop; // ファイルの先頭に移動する

/// 先頭の空白行をスキップする

 golinetop; // 行頭に移動する
 while(code == 0x0D)  // 0x0Dは、改行文字を表す。
 {
  down 1; // 次の行に移動する
  golinetop; // 行頭に移動する
 } 
 
 message "先頭の空白行をスキップ" +str(lineno);
 
while(code != eof)
{
 message "タブを挿入" + str(lineno);
/// 次の空白行を探す
 golinetop; // 行頭に移動する
 while(code != 0x0D)  // 0x0Dは、改行文字を表す。
 {
  down 1; // 次の行に移動する
  golinetop; // 行頭に移動する
 } 
 up 1; // 前の行に移動する
 golineend; // 行末に移動
 delete;  // 削除
 tab; // タブ挿入
 delete;  // 削除
 


 down 1; // 次の行に移動する
 golinetop; // 行頭に移動する
 
  message "連続行に対する処理" + str(lineno); 
 /// 連続行に対する処理
 while (code != 0x0D)
 {
  backspace;              //Backspace

  down 1; // 次の行に移動する
  golinetop; // 行頭に移動する
 }

 message "空白を削除" + str(lineno);
/// 空白行を削除
 golinetop; // 行頭に移動する
 while(code == 0x0D)  // 0x0Dは、改行文字を表す。
 {
  delete;  // 削除
  golinetop; // 行頭に移動する
  message "空白を削除[ループ](Loop)" + str(lineno);
 } 
  down 1; // 次の行に移動する
 golineend; // 行末に移動
 
}    
    
********************************************

○期待するご指摘

・正常に動作しないマクロの修正点・問題点の指摘
・他の方法で処理するマクロの提示
・同様な機能の既存のマクロの紹介

自分では、解決方法が思い浮かばない状態です。
マクロや処理方法に対してご指摘・アドバイスを頂きたいと考えています。

○実行環境
使用している秀丸エディタは、8.81 64bit版です。
マクロは、Unicode (UTF-8, BOM あり[Ver8.50以降])で作成し、実行しました。

○マクロの実行方法
加工前のテキストを開いた状態で、マクロを実行します。
※エラーがあるため、終了しません。
無限ループに入るため、秀丸エディタのウィンドウをクリックして、マクロを終了して下さい。
[ ]
RE:08712 空白行区切りテキストをタブ区切りテキストNo.08713
でるもんたいいじま さん 18/07/03 19:52 [ コメントを投稿する ]
  でるもんた・いいじま@秀丸歴20年の年寄り、です。

> マクロを作成しました。

かなりの力作ですね。努力の跡がハッキリと見えます。

ただ、これを言ってしまうのはちょっと心苦しいのですが、
「C言語なら確かにこの方針で書いてもいいんだけど、
 秀丸マクロとかPerlとか、あるいは他の最近の言語なら、
 もっと格段に短くて読みやすくて、動作も速いコードが
 書けるのでは?」
と思ってしまいました。なのでゴメンナサイ、せっかく投稿して
いただいたマクロはほとんど読まずに代案を提案させていただきます。

> 途中までは、うまく動作しているようですが、
> ループの終了条件が間違っているようで、最終的には、
> 意図したテキストになりません。マクロも終了しません。

コードをざっと見る限り、
「既にファイルの末尾に来ていて、code==eofになっているのに、
 while ( code!=0x0D ) { ... } の無限ループに落ち込んでいる」
というのが原因だと思います。

#「データ終わり」を示す文字コードが EOF、\0、\n の3つある、
#ってのは、C言語とかではありがちな罠ですね。

☆ ☆ ☆

というわけで、要求仕様だけから書き起こしてみました。
まずは、「データ2つで1行」に固定のバージョン。

//- - - - - - - - - - - - - - - -  キリトリセン - - - - - - - - - - - - - - - 
setcompatiblemode 0x20000;

// Step 1: 空行なしで続いている複数行を、1行にまとめてしまう。
begingroupundo;
replaceallfast @"(?<!\n)\n(?!\n)", "", regular;
// replaceallfast @"([^\n])\n([^\n])", @"\1\2", regular; //こう書いてもよい

// 上記の置換ではファイルの最後の改行を取り除いてしまうので、付け直す。
gofileend;
insert "\n";
endgroupundo;

// Step 2: 改行区切りからタブ区切りへ一気に置換
// ※maxlinesの値は足りなければ増やしてください。
$reFROM = @"(?#maxlines:10)\n*([^\n]+)\n+([^\n]+)\n";
$reTO = @"\1\t\2\n";
begingroupundo;
replaceallfast $reFROM, $reTO, regular;
endgroupundo;
endmacro;
//- - - - - - - - - - - - - - - -  キリトリセン - - - - - - - - - - - - - - - 

で、上記のコードでは、$reFROM、$reTO という変数に正規表現を
代入していますが、この正規表現自体をマクロで編集することで、
2列ではなく任意の列数で同様のことができます。

そうすると、最終的にこんなコードになりました。

//- - - - - - - - - - - - - - - -  キリトリセン - - - - - - - - - - - - - - - 
#MAXNUM_PARENS = 15; // V8.58時点での仕様

// Step 0: 初期設定
if (version < 858 )
{
message "このマクロには、秀丸エディタV8.58以上が必要です。";
endmacro;
}
setcompatiblemode 0x20000;

// Step 1: 列数を入力してもらう
again:
#n = val( input("いくつのデータを1行にまとめますか?","2") );
if ( !result ) endmacro;
if ( #n<2 || #n>#MAXNUM_PARENS )
{
message "指定できるのは2から" + str(#MAXNUM_PARENS) + "までです。";
goto again;
}

// Step 2: 空行なしで続いている複数行を、1行にまとめてしまう。
begingroupundo;
replaceallfast @"(?<!\n)\n(?!\n)", "", regular;
// replaceallfast @"([^\n])\n([^\n])", @"\1\2", regular; //こう書いてもよい

// 上記の置換ではファイルの最後の改行を取り除いてしまうので、付け直す。
gofileend;
insert "\n";
endgroupundo;

// Step 3: 改行区切りからタブ区切りへ一気に置換するため、正規表現を生成
// ※maxlinesの値は足りなければ増やしてください

$reFROM = @"(?#maxlines:10)\n*";
#i = 1;
while ( #i < #n )
{
$reFROM = $reFROM + @"([^\n]+)\n+";
$reTO = $reTO + sprintf(@"\g{%d}\t", #i);
#i = #i+1;
}

$reFROM = $reFROM + @"([^\n]+)\n";
$reTO = $reTO + sprintf(@"\g{%d}\n", #n);
message sprintf("実行内容:\nFROM=「%s」\nTO=「%s」", $reFROM, $reTO);

// Step 4: 一気に置換!
begingroupundo;
replaceallfast $reFROM, $reTO, regular;
endgroupundo;
endmacro;
//- - - - - - - - - - - - - - - -  キリトリセン - - - - - - - - - - - - - - - 

正規表現を使いこなせるようになると、1文字単位の処理では面倒すぎる
処理が簡単にできてしまって、しかもスピードも格段に速くなります。
(正規表現どおりに検索・置換する処理は、C言語で既に徹底的に
 チューニングされています。したがって、同じ処理を下手に
 スクラッチから書くよりも「早い」上に「速い」のです。)
kukeさんもぜひ、正規表現を使いこなせるようになってください。
[ ]
RE:08713 空白行区切りテキストをタブ区切りテキストNo.08714
kuke さん 18/07/03 23:17 [ コメントを投稿する ]
  でるもんた・いいじま さん

kukeです。ありがとうございました。動きました。助かりました。

処理の流れがはっきりしていて「なるほど」と思いました。

正規表現を使うことを選択したなったのは、以前、htmlテキストのheadタグをまるごと削除しようとして、複数行をまたぐ一致でうまく動作しないことがありました。そこで、今回も複数行をまたぐ一致に相当するのではないかと使うのを避けました。

その結果が、先のコードなのですが、
削除したり、挿入したりすると、カーソルの位置が変化するので、どうなっているか理解するのがとても難しくて困りました。

その際、カーソルが、最終行にあることを確認する関数やプロパティがあればと思いました。

これから、正規表現を積極的に使うように心がけてみます。
[ ]

[ 新規に投稿する ]