替换后正确的子串位置

时间:2017-10-15 10:25:31

标签: javascript string

我有一个这样的功能,由用户提供:

function replace_function(string) {
    return string.replace(/:smile:/g, '⻇')
      .replace(/(foo|bar|baz)/g, 'text_$1');
}

我输入了这样的字符串:

var input = 'foo bar :smile: xxxx';

我有一个从0到输入字符串长度的数字,我用来做子串来分割字符串。

我需要找到替换后匹配输出字符串的数字(位置),以便拆分位于同一个可视位置。拆分仅用于可视化我只需要数字。

输出字符串可以具有相同的长度,这仅适用于输入和输出长度不同的情况(如宽度提供的函数和输入字符串)



function replace_function(string) {
    return string.replace(/:smile:/g, '⻇')
      .replace(/(foo|bar|baz)/g, 'text_$1');
}

var textarea = document.querySelector('textarea');
var pre = document.querySelector('pre');
function split() {
  var input = textarea.value;
  var output = replace_function(input);
  // find position for output
  var position = textarea.selectionStart;
  var split = [
    output.substring(0, position),
    output.substring(position)
  ];
  pre.innerHTML = JSON.stringify(split);
}
textarea.addEventListener('click', split);

<textarea>xxx foo xxx bar xxx :smile: xxxx</textarea>
<pre></pre>
&#13;
&#13;
&#13;

当您单击要替换的单词的中间时,位置/拆分需要在输出单词之后。如果您在单词之前,之间或之后单击,则位置需要位于相同位置(每种情况下的位置将不同以匹配正确的位置)

更新:这是我的代码,适用于:smile:只输入,但之前需要有文字:smile :(输入=&#34;:smile:asdas&#34;并且在微笑中间位置并且位置关闭)

它适用于foo替换为text_foo但不适用于以下情况:smile:foo之前(输入&#34; asd:smile:asd foo&#34;)。

&#13;
&#13;
var get_position = (function() {
  function common_string(formatted, normal) {
      function longer(str) {
          return found && length(str) > length(found) || !found;
      }
      var formatted_len = length(formatted);
      var normal_len = length(normal);
      var found;
      for (var i = normal_len; i > 0; i--) {
          var test_normal = normal.substring(0, i);
          var formatted_normal = replace_function(test_normal);
          for (var j = formatted_len; j > 0; j--) {
              var test_formatted = formatted.substring(0, j);
              if (test_formatted === formatted_normal &&
                  longer(test_normal)) {
                  found = test_normal;
              }
          }
      }
      return found || '';
  }
  function index_after_formatting(position, command) {
      var start = position === 0 ? 0 : position - 1;
      var command_len = length(command);
      for (var i = start; i < command_len; ++i) {
          var substr = command.substring(0, i);
          var next_substr = command.substring(0, i + 1);
          var formatted_substr = replace_function(substr);
          var formatted_next = replace_function(next_substr);
          var substr_len = length(formatted_substr);
          var next_len = length(formatted_next);
          var test_diff = Math.abs(next_len - substr_len);
          if (test_diff > 1) {
              console.log('return ' + i);
              return i;
          }
      }
  }
  return function get_formatted_position(position, command) {
      var formatted_position = position;
      var string = replace_function(command);
      var len = length(string);
      var command_len = length(command);
      if (len !== command_len) {
          var orig_sub = command.substring(0, position);
          var orig_len = length(orig_sub);
          var sub = replace_function(orig_sub);
          var sub_len = length(sub);
          var diff = Math.abs(orig_len - sub_len);
          if (false && orig_len > sub_len) {
              formatted_position -= diff;
          } else if (false && orig_len < sub_len) {
              formatted_position += diff;
          } else {
              var index = index_after_formatting(position, command);
              var to_end = command.substring(0, index + 1);
              //formatted_position -= length(to_end) - orig_len;
              formatted_position -= orig_len - sub_len;
              if (orig_sub && orig_sub !== to_end) {
                  var formatted_to_end = replace_function(to_end);
                  var common = common_string(formatted_to_end, orig_sub);
                  var re = new RegExp('^' + common);
                  var before_end = orig_sub.replace(re, '');
                  var to_end_rest = to_end.replace(re, '');
                  var to_end_rest_len = length(replace_function(to_end_rest));
                  if (before_end && orig_sub !== before_end) {
                      var commnon_len = length(replace_function(common));
                      formatted_position = position - length(before_end) + to_end_rest_len;
                  }
              }
          }
          if (formatted_position > len) {
              formatted_position = len;
          } else if (formatted_position < 0) {
              formatted_position = 0;
          }
      }
      return formatted_position;
  };
})();

function length(str) {
    return str.length;
}

function replace_function(string) {
    return string.replace(/:smile:/g, '⻇')
      .replace(/(foo|bar|baz)/g, 'text_$1');
}

var textarea = document.querySelector('textarea');
var pre = document.querySelector('pre');
function split() {
  var input = textarea.value;
  var output = replace_function(input);
  // find position for output
  var position = get_position(textarea.selectionStart, input);
  var split = [
    output.substring(0, position),
    output.substring(position)
  ];
  pre.innerHTML = JSON.stringify(split);
}
textarea.addEventListener('click', split);
&#13;
<textarea>xxxx :smile: xxxx :smile: xxx :smile:</textarea>
<pre></pre>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:1)

要做到这一点,你必须自己使用replace循环执行RegExp#exec操作,并跟踪替换如何影响位置,这些内容(但这可能是优化):

function trackingReplace(rex, string, replacement, position) {
    var newString = "";
    var match;
    var index = 0;
    var repString;
    var newPosition = position;
    var start;
    rex.lastIndex = 0; // Just to be sure
    while (match = rex.exec(string)) {
        // Add any of the original string we just skipped
        if (rex.global) {
            start = rex.lastIndex - match[0].length;
        } else {
            start = match.index;
            rex.lastIndex = start + match[0].length;
        }
        if (index < start) {
            newString += string.substring(index, start);
        }
        index = rex.lastIndex;
        // Build the replacement string. This just handles $$ and $n,
        // you may want to add handling for $`, $', and $&.
        repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
            if (c0 == "$") return "$";
            return match[c0];
        });
        // Add on the replacement
        newString += repString;
        // If the position is affected...
        if (start < position) {
            // ... update it:
            if (rex.lastIndex < position) {
                // It's after the replacement, move it
                newPosition = Math.max(0, newPosition + repString.length - match[0].length);
            } else {
                // It's *in* the replacement, put it just after
                newPosition += repString.length - (position - start);
            }
        }

        // If the regular expression doesn't have the g flag, break here so
        // we do just one replacement (and so we don't have an endless loop!)
        if (!rex.global) {
            break;
        }
    }
    // Add on any trailing text in the string
    if (index < string.length) {
        newString += string.substring(index);
    }
    // Return the string and the updated position
    return [newString, newPosition];
}

这是一个片段,显示我们在不同的位置测试:

function trackingReplace(rex, string, replacement, position) {
    var newString = "";
    var match;
    var index = 0;
    var repString;
    var newPosition = position;
    var start;
    rex.lastIndex = 0; // Just to be sure
    while (match = rex.exec(string)) {
        // Add any of the original string we just skipped
        if (rex.global) {
            start = rex.lastIndex - match[0].length;
        } else {
            start = match.index;
            rex.lastIndex = start + match[0].length;
        }
        if (index < start) {
            newString += string.substring(index, start);
        }
        index = rex.lastIndex;
        // Build the replacement string. This just handles $$ and $n,
        // you may want to add handling for $`, $', and $&.
        repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
            if (c0 == "$") return "$";
            return match[c0];
        });
        // Add on the replacement
        newString += repString;
        // If the position is affected...
        if (start < position) {
            // ... update it:
            if (rex.lastIndex < position) {
                // It's after the replacement, move it
                newPosition = Math.max(0, newPosition + repString.length - match[0].length);
            } else {
                // It's *in* the replacement, put it just after
                newPosition += repString.length - (position - start);
            }
        }

        // If the regular expression doesn't have the g flag, break here so
        // we do just one replacement (and so we don't have an endless loop!)
        if (!rex.global) {
            break;
        }
    }
    // Add on any trailing text in the string
    if (index < string.length) {
        newString += string.substring(index);
    }
    // Return the string and the updated position
    return [newString, newPosition];
}

function show(str, pos) {
    console.log(str.substring(0, pos) + "|" + str.substring(pos));
}
function test(rex, str, replacement, pos) {
    show(str, pos);
    var result = trackingReplace(rex, str, replacement, pos);
    show(result[0], result[1]);
}
for (var n = 3; n < 22; ++n) {
    if (n > 3) {
       console.log("----");
    }
    test(/([f])([o])o/g, "test foo result foo x", "...$2...", n);
}
.as-console-wrapper {
  max-height: 100% !important;
}

这是您的代码段更新后使用它:

function trackingReplace(rex, string, replacement, position) {
    var newString = "";
    var match;
    var index = 0;
    var repString;
    var newPosition = position;
    var start;
    rex.lastIndex = 0; // Just to be sure
    while (match = rex.exec(string)) {
        // Add any of the original string we just skipped
        if (rex.global) {
            start = rex.lastIndex - match[0].length;
        } else {
            start = match.index;
            rex.lastIndex = start + match[0].length;
        }
        if (index < start) {
            newString += string.substring(index, start);
        }
        index = rex.lastIndex;
        // Build the replacement string. This just handles $$ and $n,
        // you may want to add handling for $`, $', and $&.
        repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
            if (c0 == "$") return "$";
            return match[c0];
        });
        // Add on the replacement
        newString += repString;
        // If the position is affected...
        if (start < position) {
            // ... update it:
            if (rex.lastIndex < position) {
                // It's after the replacement, move it
                newPosition = Math.max(0, newPosition + repString.length - match[0].length);
            } else {
                // It's *in* the replacement, put it just after
                newPosition += repString.length - (position - start);
            }
        }

        // If the regular expression doesn't have the g flag, break here so
        // we do just one replacement (and so we don't have an endless loop!)
        if (!rex.global) {
            break;
        }
    }
    // Add on any trailing text in the string
    if (index < string.length) {
        newString += string.substring(index);
    }
    // Return the string and the updated position
    return [newString, newPosition];
}

function replace_function(string, position) {
    var result = trackingReplace(/:smile:/g, string, '⻇', position);
    result = trackingReplace(/(foo|bar|baz)/g, result[0], 'text_$1', result[1]);
    return result;
}

var textarea = document.querySelector('textarea');
var pre = document.querySelector('pre');
function split() {
  var position = textarea.selectionStart;
  var result = replace_function(textarea.value, position);
  var string = result[0];
  position = result[1];
  var split = [
    string.substring(0, position),
    string.substring(position)
  ];
  pre.innerHTML = JSON.stringify(split);
}
textarea.addEventListener('click', split);
<textarea>:smile: foo</textarea>
<pre></pre>