検索ヒット数表示(選択文字列)
ks 氏の「includeライブラリ」の StringEx.js(count 関数)と「検索ヒット数表示」マクロをもとに作成。
単独マクロバージョン
- アクティブなタブの文書内から選択範囲の文字列に 完全一致 するものをカウントする。
- ヒット件数はステータスバーに表示する。
選択範囲のある状態でマクロを手動で実行するか、「選択範囲が変更されたとき」のイベントマクロに設定して自動で実行させるのがよろしいかと。
※ このマクロと Mery 標準の検索メソッドとでは、ヒット数のカウントの仕方が異なります。
- Mery の検索ではヒットした文字列の「先頭位置より1文字うしろ」から再検索しますが、このマクロではヒットした文字列の「末尾位置」から再検索します。
- e.g.「あああああ」5文字だけの文書で「ああ」を検索した場合、このマクロでは "2件" とカウントしますが、Mery の検索メソッド(次/前を検索)では "4回" ヒットします。
また、「あいあいあ」5文字だけの文書で「あいあ」を検索した場合は、このマクロでは "1件" とカウントします(Mery の検索メソッドでは "2回" ヒット)。 - → 「何件目」かの表示は、上の例のような範囲選択をしている場合には不正確な値を返します。
※ このマクロの実行にあたって include ライブラリは不要です。
※ このマクロでは、イベントマクロで利用する場合の処理速度を優先させるために「検索ヒット数表示 (JScript)」 マクロのような正規表現検索には対応させていません。また、文字列のヒット判定では「大文字と小文字を区別する」が適用され、「単語のみを検索する」は適用されません。
※ イベントマクロで利用する場合は「遅延時間」を適宜調整しないと、大きな文書の編集のさいに動作が "もたつく" ことがあります。
オプション指定をつけてヒット数を調べたい場合は、「ポップアップメニューで検索先にジャンプ 」マクロの include 版をご利用ください(イベントマクロにはできませんが)。
※ 高度な(煩雑な)動作設定が可能な 機能強化バージョン を追加(2019/08/26)
ソースコード
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万文字の文書では "もたつき" が生じましたが…。
ソースコード
2019/08/28 更新
#title = "文字数・行数・ヒット件数(選択文字列)"
#tooltip = "文字数・行数・検索ヒット数表示(選択範囲の文字列)"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",19
/**
* --------------------------------------------------------------------
* 文字数・行数・ヒット数
* sukemaru ( 2019/08/26 - 2019/08/28 )
* --------------------------------------------------------------------
* 編集中の文書または選択範囲の文字数と行数をステータスバーに表示します。
* 選択範囲があるときは、選択範囲の文字列の出現回数を表示します。
*
* 選択範囲のある状態でマクロを手動で実行するか、
* イベント「選択範囲が変更された時」に設定して自動で実行させる想定です。
*
* ※ ソースコード末尾に動作仕様(制限事項)の説明あり。
*/
var timerStart = new Date();
var d = editor.ActiveDocument;
// ---------- ▼ 設定項目 ▼ ----------
// ■ 行数のカウント方法
// true:論理行(改行) / false:物理行(折り返し)
var logical = false; // 初期値: false( ← Mery 方式)
// ■ 選択範囲の末尾が改行記号のときの 行数のカウント方法
// true:次の行頭を含めない / false:含める
var ignoreEnter = false; // 初期値: false( ← Mery 方式)
// ■ 文字数のカウント方法 での改行記号 CR+LF の数え方
// true:2文字あつかい / false:1文字あつかい
var countCRLF = false; // 初期値: false( ← Mery 方式)
// ■ ヒット数のカウント方法
// true:大文字と小文字を区別する / false:区別しない
var matchCase = false; // 初期値: false( ← 自動マーカーの初期値)
// ■ 「あああああ」 5文字だけの文書で 「ああ」 を範囲選択したときのカウント方法
// true:ヒット数 2件 / false:ヒット数 4件
var countMinimum = false; // 初期値: false( ← Mery 方式)
// ■ 「すべて選択」でファイルサイズを表示
// true:表示する / false:表示しない
var bytesEnable = true; // 初期値: true
// ■ マクロの処理の所要時間を表示
// true:表示する / false:表示しない
var timerEnable = false; // 初期値: false
/* 未保存文書のバイト数の計算用 */
// ■ 文字コードの形式の指定
// "" なら文書の文字コード形式 / 指定時はその文字コード(e.g. "sjis", "utf-8")
var encoding = ""; // 初期値: 自動
// ■ 改行コードの形式の指定
// true: CR+LF / false: CR or LF / 「d.LineEnding == 0」: 自動( ← 要 Mery 2.6.10 以上)
var CRLF = ( d.LineEnding == 0 ); // 初期値: 自動
// ■ BOM コード分を加算するかの指定
// true: BOM を考慮する / false: 考慮しない
var BOM = false; // 初期値: false
// ---------- ▲ 設定項目 ▲ ----------
var s = d.selection;
var isEmpty = s.IsEmpty; // 「範囲選択なし」のフラグ
var selAll = false; // 「すべて選択」のフラグ
var posMode = logical ? mePosLogical : mePosView;
var lineMode = logical ? 0 : meGetLineView;
var dt = d.Text;
var lines;
var st;
var statusStr;
// 範囲選択なしなら文書全体の文字数と行数(論理行 or 表示行)
if ( isEmpty ) {
st = dt;
lines = d.GetLines( lineMode );
}
// 範囲選択ありなら選択範囲の文字数と行数(論理行 or 表示行)
else {
st = s.Text;
selAll = ( st == dt );
lines = s.GetBottomPointY( posMode )
- s.GetTopPointY( posMode )
+ ( ( ignoreEnter && /[\r\n]$/.test( st ) ) ? 0 : 1 );
}
// 文字数( CR+LF 補正 )
var isCRLF = ( countCRLF && CRLF );
var sLen = st.length
+ ( isCRLF ? ( st.match( /\n/g ) || [] ).length : 0 );
/* 範囲選択なしのときは 文書全体の文字数・行数 を、
ありのときは 選択範囲の文字数・行数 をステータスバーに表示 */
var charStr = " " + sLen + " 文字";
var lineStr = " ( " + lines + " 行 )";
statusStr = charStr + lineStr;
// すべて選択のときはファイルサイズを取得する
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
var fileSize = ( bytesEnable && selAll )
? d.Saved
? fso.GetFile( d.FullName ).Size // ファイルプロパティから
: CalculateBytesKs( dt, d ) // ks 版「バイト数」マクロ
: 0;
var fileSizeStr = fileSize
? " ( ファイルサイズ: " + fileSize + " バイト )"
: "";
/* ファイルサイズ、または選択文字列のヒット数をステータスバーに追加 */
statusStr += ( isEmpty || selAll )
? fileSizeStr
: HitStatus( st, dt, matchCase, countMinimum );
/* マクロの処理の所要時間をステータスバーに追加 */
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, matchCase, countMinimum )
* 選択文字列の出現回数を返す
* ※ 変数 s はグローバルスコープの Document.Selection
*/
function HitStatus( word, docu, matchCase, countMinimum ) {
var word = matchCase ? word : word.toLowerCase();
var docu = matchCase ? docu : docu.toLowerCase();
var len = word.length;
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 += countMinimum ? len : 1;
}
}
return " / ヒット数:" + count + " 件"
+ " ( " + hit + " 件目 ) ";
}
/**
* 関数 CalculateBytesKs( str, objDocu )
* ks 版「バイト数」マクロのコードを関数化
* ※ グローバルスコープの変数を利用する
*/
function CalculateBytesKs( str, objDocu ) {
var dEnc = objDocu.Encoding;
str = ( CRLF == true ) ? str.replace( /\r?\n/g, "\r\n" ) : str;
var charset = "", bytes = 0;
if ( ! encoding ) {
switch ( dEnc ) {
case meEncodingUTF16LENoBOM: case meEncodingUTF16BENoBOM:
bytes = str.length * 2;
return bytes;
case meEncodingUTF16LEBOM: case meEncodingUTF16LE:
case meEncodingUTF16BEBOM: case meEncodingUTF16BE:
bytes = ( str.length * 2 ) + ( BOM ? 2 : 0 );
return bytes;
case meEncodingUTF8BOM: case meEncodingUTF8NoBOM:
charset = "utf-8"; break;
case meEncodingUTF7:
charset = "utf-7"; break;
case meEncodingEUC:
charset = "euc-jp"; break;
case meEncodingJIS:
charset = "iso-2022-jp"; break;
case meEncodingShiftJIS:
charset = "sjis"; break;
}
}
else { charset = encoding; }
var adodb = new ActiveXObject( "ADODB.Stream" );
adodb.Type = 2, adodb.Charset = charset;
adodb.Open();
adodb.WriteText( str );
bytes = adodb.Position;
adodb.Close();
if ( ! encoding &&
( dEnc == meEncodingUTF8NoBOM ||
( ! BOM && dEnc == meEncodingUTF8BOM ) ) ) {
bytes -= 3;
}
return bytes;
}
/** * イベント「選択範囲が変更された時」以外にも、 * 「ファイルを開いた時」「テキストが変更された時」「文字が挿入された時」 * 「アクティブな文書が変更された時」など、任意で。 * * ※ イベントの設定で「遅延時間」を適宜調整すること * (とくに「カーソルが移動した時」を適用する場合は遅延を長めに)。 * * 「ヒット数のカウント方法」の設定項目「大文字と小文字を区別」は、 * 自動マーカーの設定状態(初期値:false)にあわせるのがよろしいかと。 * 表示メニュー >> マーカー >> マーカーの設定... >> オプション * * * ▼ 動作仕様 / 制限事項 ▼ * * ※ 矩形選択範囲では正しく機能しません。 * * ※ 各設定項目で無効(false)を "Mery 方式" としているのは、 * ステータスバーの中央右寄りに表示される "選択範囲" の数え方にあわせた表現です。 * 設定項目 ignoreEnter を有効(true)にしても、 * 文字数のカウントでは選択範囲末尾の改行記号を含めます。 * * ※ 文字数のカウントでは、改行記号の * CR+LF と CR と LFの区別なしに1文字として扱います( ← Mery 方式)。 * → 設定変数で区別可能にした(※ 推奨要件: Mery ver 2.6.10 以上) * * よそのエディタの 表示/カウント 方式にあわせたい場合は、 * 任意の項目を有効化(true)してください。 * * ※ 「あああああ」5文字だけの文書で「ああ」を範囲選択した場合、 *このマクロではヒット数を "2件" としてカウントします* (Mery の検索メソッドでは "4回" ヒットする)。 * また、「あ あ あ」5文字だけの文書で「あ あ」を検索した場合は、 *"1件" とカウントします(Mery の検索では "2回" ヒット)。 * → 設定変数で切り替え可能にした * ただし、countMinimum = true で上の例のような範囲選択をしている場合、 * 「n件目」表示は不正確な値を返します。 * * ※ 設定項目の bytesEnable が有効(true)で「すべて選択」状態のときのみ、 *実体ファイルのファイルサイズを表示できます。 * → 「無題」や「未保存*」のタブでは文字数から「バイト数」を計算するようにした * * 「すべて選択」以外の状態でもバイト数を計算させることはできますが、 * イベントマクロとして使うには適さないので… (とくにロースペ PC では) */