検索ヒット数表示(選択文字列)

提供: MeryWiki
2019年8月26日 (月) 23:19時点におけるSukemaru (トーク | 投稿記録)による版 (機能強化バージョン「文字数・行数・ヒット件数(選択文字列)」を追加)
ナビゲーションに移動 検索に移動

ks 氏の「includeライブラリ」の StringEx.js(count 関数)と「検索ヒット数表示」マクロをもとに作成。

単独マクロバージョン

  • アクティブなタブの文書内から選択範囲の文字列に 完全一致 するものをカウントする。
  • ヒット件数はステータスバーに表示する。

選択範囲のある状態でマクロを手動で実行するか、「選択範囲が変更されたとき」のイベントマクロに設定して自動で実行させるのがよろしいかと。


※ このマクロと Mery 標準の検索メソッドとでは、ヒット数のカウントの仕方が異なります。

e.g.「あああああ」5文字だけの文書で「ああ」を検索した場合、このマクロでは "2件" とカウントしますが、Mery の検索メソッド(次/前を検索)では "4回" ヒットします。
また、「あいあいあ」5文字だけの文書で「あいあ」を検索した場合は、このマクロでは "1件" とカウントします(Mery の検索メソッドでは "2回" ヒット)。
→ 「何件目」かの表示は、上の例のような範囲選択をしている場合には不正確な値を返します。


※ このマクロの実行にあたって include ライブラリは不要です。

※ このマクロでは、イベントマクロで利用する場合の処理速度を優先させるために「検索ヒット数表示 (JScript)」 マクロのような正規表現検索には対応させていません。また、文字列のヒット判定では「大文字と小文字を区別する」が適用され、「単語のみを検索する」は適用されません。

※ イベントマクロで利用する場合は「遅延時間」を適宜調整しないと、大きな文書の編集のさいに動作がもたつくことがあります。

オプション指定をつけてヒット数を調べたい場合は、「ポップアップメニューで検索先にジャンプ 」マクロの include 版をご利用ください(イベントマクロにはできませんが)。

ソースコード

2019/07/20 更新

  • 全体のヒット件数のうしろに「何件目」のヒットかを追加
#title = "ヒット件数(選択文字列)"
#tooltip = "検索ヒット数表示(選択範囲の文字列)"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",19

/**
 * アクティブなタブの文書内から選択範囲の文字列に完全一致するものをカウントする。
 * ヒット件数はステータスバーに表示する。
 * 
 * 選択範囲のある状態でマクロを手動で実行するか
 * イベント「選択範囲が変更されたとき」に設定して自動で実行させる。
 * 
 * ※ ks 氏の『includeライブラリ』より
 *     >> include/StringEx.js >> String.prototype.count() 
 *   のコードを拝借した。
 */

var timerStart = new Date();
Status = "";

var d = editor.ActiveDocument;
var s = d.selection;
var word = s.Text;

if ( word ) {
  var len = word.length;
  var dText = d.Text;

  // 大文字/小文字の区別なしにするなら、次の2行をアンコメントせよ
  // word = word.toLowerCase();
  // dText = dText.toLowerCase();

  var tPos = Math.min( s.GetActivePos(), s.GetAnchorPos() )
  var pos = 0,  count = 0,  hit = 0;
  while ( pos >= 0 ) {
    pos = dText.indexOf( word, pos );
    if ( pos >= 0 ) {
      count ++;
      if ( pos <= tPos  ) {
        hit = count;
      }
      pos += len;
    }
  }
  var SeparateNum = function( num ) {
    return num.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," );
  }
  Status = " ヒット数:" + SeparateNum( count ) + " 件"
         + " ( " + SeparateNum( hit ) + " 件目 ) ";
  var TimerElapsed = function( end, start ) {
    var elapsedSec = ( ( end - start ) / 1000 ).toFixed( 3 );
    return "  [ " + elapsedSec.replace( /\./, ". " ) + " 秒 ]";
  }
  // 検索所要時間の表示が不要なら、次の1行をコメントアウトせよ
  Status += TimerElapsed( new Date(), timerStart );
}


組み込み用関数バージョン

2019/07/20 追加

  • 引数に指定した文字列の出現回数 "ヒット数:n 件 ( m 件目 ) " を返す。

※「バイト数」マクロなどへの追加コードとして利用できます。

※ 引数が「選択範囲の文字列」でない場合は、キャレット位置までのヒット数を「m 件目」として返します(ただし、キャレットのすぐ右に指定文字列がある場合は、『m 件目』の値 + 1 で返します)。

※ その他、仕様・注意事項については単独マクロバージョンに準じます。

使用例

// 例. ステータスバーの表示内容に追加
if ( document.selection.Text ) {
  Status += "  " + HitStatus( document.selection.Text );
}

組み込み関数コード

/* 
 * 関数 HitStatus( str )
 * 引数に指定した文字列の出現回数 "ヒット数:n 件 ( m 件目 ) " を返す
 * 引数 str: 選択範囲の文字列などの「文字列」オブジェクト
 * ※「大文字/小文字の区別なし」にしたい場合は、コメントアウトされた toLowerCase の行をアンコメントする
 */
function HitStatus( str ) {
  if ( str ) {
    // str = str.toLowerCase();
    var d = editor.ActiveDocument,  s = d.selection;
    var dText = d.Text;
    // dText = d.Text.toLowerCase();
    var tPos = Math.min( s.GetActivePos(), s.GetAnchorPos() );
    var len = str.length,  pos = 0,  count = 0,  hit = 0;
    while ( pos >= 0 ) {
      pos = dText.indexOf( str, pos );
      if ( pos >= 0 ) {
        count ++;
        if ( pos <= tPos  ) { hit = count; }
        pos += len;
      }
    }
    var SeparateNum = function( num ) {
      return num.toString().replace( /(\d)(?=(?:\d{3})+$)/g, "$1," );
    }
    return "ヒット数:" + SeparateNum( count ) + " 件"
           + " ( " + SeparateNum( hit ) + " 件目 ) ";
  }
}


機能強化バージョン

2019/08/26 追加

単独マクロバージョン に以下の機能を追加しています。

  • 選択範囲の文字数と行数を表示
  • 保存済みの文書で「すべて選択」しているときにファイルサイズを表示
  • 設定項目から表示方法や検索方法などを変更可


※ ロースペックの PC でイベントマクロ(選択範囲が変更されたとき/遅延 0秒)として使用しても、1万行・30万文字程度の文書では編集作業のジャマにはならないようでした。
6万行・800万文字の文書では "もたつき" が生じましたが…。

ソースコード

#title = "文字数・行数・ヒット件数(選択文字列)"
#tooltip = "文字数・行数・検索ヒット数表示(選択範囲の文字列)"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",19

/**
 * --------------------------------------------------------------------
 * 文字数・行数・ヒット数
 * sukemaru ( 2019/08/26 )
 * --------------------------------------------------------------------
 * 編集中の文書または選択範囲の文字数と行数をステータスバーに表示します。
 * 選択範囲があるときは、選択範囲の文字列の出現回数を表示します。
 * 
 * 選択範囲のある状態でマクロを手動で実行するか、
 * イベント「選択範囲が変更されたとき」に設定して自動で実行させる想定。
 * 
 * ▼ 制限事項 ▼
 * 
 * ※ 矩形選択範囲では正しく機能しません。
 * 
 * ※ 「行数のカウント方法」の設定項目 Logical と ignoreEnter で
 *    無効(false)を「Mery 方式」としているのは、
 *    ステータスバーの中央右寄りに表示される "選択範囲の行数" の数え方に
 *    合せるための表現です。
 *    ignoreEnter を有効(true)にしても、
 *    文字数のカウントには選択範囲末尾の改行記号を含めます。
 * 
 * ※ 文字数のカウントでは、改行コードの
 *    CR+LF と CR と LF の区別なしに1文字として扱います。
 * 
 * ※ 「あああああ」5文字だけの文書で「ああ」を検索した場合、
 *    このマクロでは "2件" とカウントします。
 *    また、「あ あ あ」5文字だけの文書で「あ あ」を検索した場合は、
 *    このマクロでは "1件" とカウントします。
 * 
 * ※ 設定項目の bytesEnable が有効(true)で「すべて選択」状態のときのみ、
 *    実体ファイルのファイルサイズを表示できます。
 *    ただし、「無題」や「未保存状態*」のタブでは表示しません。
 */


// ---------- ▼ 設定項目 ▼ ----------

// ■ 行数のカウント方法
// true:論理行(改行) / false:物理行(折り返し ← Mery 方式)
var logical = false;

// ■ 選択範囲の末尾が改行記号のときの行数のカウント方法
// true:次の行頭を含めない / false:含める( ← Mery 方式)
var ignoreEnter = false;

// ■ ヒット数のカウント方法
// true:大文字と小文字を区別しない / false:区別する
var ignoreCaseEnable = true;

// ■ ファイルサイズの表示
// true:表示する / false:表示しない
var bytesEnable = true;

// ■ マクロの処理の所要時間の表示
// true:表示する / false:表示しない
var timerEnable = false;

// ---------- ▲ 設定項目 ▲ ----------


var timerStart = new Date();
var d = editor.ActiveDocument;
var s = d.selection;
var isEmpty = s.IsEmpty;	// 「範囲選択なし」のフラグ
var selAll = false;			// 「すべて選択」のフラグ
var posMode = logical ? mePosLogical : mePosView;
var lineMode = logical ? 0 : meGetLineView;
var dLines = d.GetLines( lineMode );
var sLines;
var dt = d.Text;
var st;
var statusStr;

// 範囲選択なしなら文書全体の文字列と行数
if ( isEmpty ) {
  st = dt;
}
// 範囲選択ありなら選択範囲の文字列と行数
else {
  st = s.Text;
  selAll = ( st == dt );
  sLines = s.GetBottomPointY( posMode )
         - s.GetTopPointY( posMode )
         + ( ( ignoreEnter && /[\r\n]$/.test( st ) ) ? 0 : 1 );
}
var sLen = st.length;

// 範囲選択なしのときは、文書全体の文字数・行数を、
// 範囲選択ありのときは、選択範囲の文字数・行数をステータスバーに表示
var charStr = " " + sLen + " 文字";
var lineStr = " ( " + ( isEmpty ? dLines : sLines ) + " 行 )";

statusStr = charStr + lineStr;

// すべて選択のときはファイルサイズを取得する
var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
var fileSize = ( bytesEnable && selAll && d.FullName && d.Saved )
                 ? Fso.GetFile( d.FullName ).Size	
                 : "";
var fileSizeStr = " ( ファイルサイズ: " + fileSize + " バイト )";

// ファイルサイズ、または選択範囲の文字列のヒット数をステータスバーに追加
statusStr += ( isEmpty || selAll ) 
               ? ( fileSize ? fileSizeStr : "" )
               : HitStatus( st, dt, ignoreCaseEnable );
statusStr += timerEnable ? TimerElapsed( new Date(), timerStart ) : "";

Status = SeparateNum( statusStr );

// --------------------------------------------------------------------

/**
 * 関数 SeparateNum( str )
 * 文字列中の数字を3ケタ区切りに
 */
function SeparateNum( str ) {
  return str.replace( /(\d)(?=(?:\d{3})+[^\d])/g, "$1," );
}

/**
 * 関数 TimerElapsed( end, start )
 * マクロの処理の所要時間を計測
 */
function TimerElapsed( end, start ) {
  var elapsedSec = ( ( end - start ) / 1000 ).toFixed( 3 );
  return " [ " + elapsedSec.replace( /\./, ". " ) + " 秒 ]";
}

/**
 * 関数 HitStatus( strSelection, strDocuText, ignoreCase )
 * 選択文字列の出現回数を返す
 */
function HitStatus( word, docu, i ) {
  word = i ? word.toLowerCase() : word;
  var len = word.length;
  var docu = i ? docu.toLowerCase() : docu;
  var tPos = Math.min( s.GetActivePos(), s.GetAnchorPos() );
  var pos = 0,  count = 0,  hit = 0;
  while ( pos >= 0 ) {
    pos = docu.indexOf( word, pos );
    if ( pos >= 0 ) {
      count ++;
      if ( pos <= tPos  ) {
        hit = count;
      }
      pos += len;
    }
  }
  return "  /  ヒット数:" + count + " 件"
         + " ( " + hit + " 件目 ) ";
}
スポンサーリンク