Skip to content

Instantly share code, notes, and snippets.

@think49
Last active May 2, 2021 07:23
Show Gist options
  • Save think49/53b4a2cedfcff3c1a1ea40390a7ff3d8 to your computer and use it in GitHub Desktop.
Save think49/53b4a2cedfcff3c1a1ea40390a7ff3d8 to your computer and use it in GitHub Desktop.
find-corresponding-from-string.js: 「開始文字列」と「終了文字列」で括られた対応関係を最長一致でマッチします。

find-corresponding-from-string.js

概要

「開始文字列」と「終了文字列」で括られた対応関係を最長一致でマッチします。 テキストエディタでよくある「対応する括弧の強調表示」と同じアルゴリズムだと思って頂ければ、イメージしやすいかもしれません。

findCorrespondingFromString (string, startPattern, endPattern)

  • 第一引数 … 検索対象文字列。
  • 第二引数 … 開始文字列(正規表現パターン)。
  • 第三引数 … 終了文字列(正規表現パターン)。

第二引数、第三引数は1文字の場合に限り、正規表現メタキャラクタをエスケープしなくても構いません(1文字で成立するメタキャラクタが存在しない為)。

使い方

対応する括弧 「」 を抽出します。

/**
 * 「xxx」
 */
var string = '「「「a」, 「「b」」, 「「c」「d」」, 「「「e」「f」」」, 「「「g」」「「h」」」';
findCorrespondingFromString(string, '「', '」'); // ["「a」","「「b」」","「「c」「d」」","「「「e」「f」」」","「「「g」」「「h」」」"]

対応する括弧 <<>> を抽出します。

/**
 * <<xxx>>
 */
var string = '<<a>>,<<<<b>>>>,<<<<c>><<d>>>>';
findCorrespondingFromString(string, '<<', '>>'); // ["<<a>>","<<<<b>>>>","<<<<c>><<d>>>>"]

対応するHTMLタグ <div></div> を抽出します。

/**
 * <div>xxx</div>
 */
var string = `<div id="parent">
  <p>text</p>
  <div class="child">
    <p>text</p>
    <p>text</p>
    <div class="grandson">
      <p>text</p>
    </div>
    <div class="grandson">grandson2</div>
  </div>
  <div class="child">child2</div>
</div>`;
findCorrespondingFromString(string, '<div(?:\\s[^>]*)?>', '</div>'); // [<div id="parent">...</div>]
/**
* find-corresponding-from-string.js
*
* @version 1.0.0
* @author think49
* @url https://gist.github.com/think49/53b4a2cedfcff3c1a1ea40390a7ff3d8
* @license http://www.opensource.org/licenses/mit-license.php (The MIT License)
*/
'use strict';
var findCorrespondingFromString = (function (String, RegExp) {
return function findCorrespondingFromString (string, startPattern, endPattern) {
var startString, endString, others,
startReg, afterReg,
startCount, endCount,
startResult, afterResult,
lastIndex,
matchedString, matches = [];
string = String(string);
startString = String(startPattern);
endString = String(endPattern);
if (startString.length === 1 && endString.length === 1) {
others = '[^' + startString.replace(/(?=^])/g, '\\') + endString.replace(/(?=])/g, '\\') + ']*';
startPattern = startString.replace(/(?=[$()*+\-.?\[\]^{|}\\])/g, '\\');
endPattern = endString.replace(/(?=[$()*+\-.?\[\]^{|}\\])/g, '\\');
startReg = new RegExp(startPattern, 'g');
afterReg = new RegExp('(?:' + others + startPattern + ')*' + others + endString.replace(/(?=[$()*+\-.?\[\]^{|}\\])/g, '\\'), 'g');
} else {
others = '(?:(?!' + startPattern + '|' + endPattern + ')[\\s\\S])*';
startReg = new RegExp(startString, 'g');
afterReg = new RegExp('(?:' + others + startPattern + ')*' + others + endPattern, 'g');
}
startMatch:
while (startResult = startReg.exec(string)) {
lastIndex = afterReg.lastIndex = startReg.lastIndex;
startCount = 1;
endCount = 0;
matchedString = startResult[0];
while (afterResult = afterReg.exec(string)) {
matchedString += afterResult = afterResult[0];
startReg.lastIndex = 0;
if (startResult = afterResult.match(startReg)) {
startCount += startResult.length;
}
if (startCount === ++endCount) {
matches.push(matchedString);
startReg.lastIndex = afterReg.lastIndex;
continue startMatch;
}
}
startReg.lastIndex = lastIndex;
}
return matches;
}
}(String, RegExp));
@think49
Copy link
Author

think49 commented May 12, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment