本文介绍了更换后纠正子串位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个由用户提供的这样的函数:

I have a function like this that's provided by a user:

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

我输入的字符串如下:

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

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

and I have a number from 0 to length of the input string that I use to do substring to split the string.

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

I need to find number (position) that will match output string after replacement so the split is in the same visual place. The split is just for visualization I only need the number.

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

The output string can have the same length, this is only for the case when length of input and output is different (like width provided function and input string)

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>

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

when you click in the middle of the word that get replaced the position/split need to be after the output word. If you click before, between or after the word the position need to be in the same place (the position in each case will be different to match the correct place)

UPDATE :这是我的代码,适用于:smile:只输入,但之前需要文本:smile:(input =:smile:asdas,位置在笑容和位置已关闭)

UPDATE: here is my code that work for :smile: only input, but there is need to be text before :smile: (input = ":smile: asdas" and position in the middle of smile and the position is off)

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

it work for foo replaced by text_foo but not in the case when there is :smile: before foo (input "asd :smile: asd foo").

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);
<textarea>xxxx :smile: xxxx :smile: xxx :smile:</textarea>
<pre></pre>

推荐答案

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

To do this, you'll have to do the replace operation yourself with a RegExp#exec loop, and keep track of how the replacements affect the position, something along these lines (but this can probably be optimized):

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];
}

这是一个片段,显示我们测试各种职位:

Here's a snippet showing us testing that with various positions:

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;
}

这里有你的代码片段更新后使用它:

And here's your snippet updated to use it:

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>

这篇关于更换后纠正子串位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-18 05:00