【问题标题】:Replace text with tokens from a list using regular expression使用正则表达式将文本替换为列表中的标记
【发布时间】:2017-05-06 08:47:55
【问题描述】:

我正在尝试使用正则表达式在 React.js 中编写一个简单的文本替换器,但我无法理解它。

所以我有一个标记列表及其相应的替换文本。我还有一个文本区域,其中包含用户编写的文本。每当一个单词环绕 { } 时,文本将被相应标记的替换文本替换。

例如,如果我的文本区域中有一个 {example},我将不得不检查我的 tokenlist 并查看列表中是否有值示例,并将 {example} 替换为列表的替换值的值。

我现在正在做的是检查我的文本区域中是否有任何匹配项:

let regEx = /\{[a-zA-Z_][a-zA-Z0-9_]*\}/;
inputText.match(regEx);

因此,我得到了一个索引和输入,但是如何用替换文本替换匹配的文本?我尝试使用替换功能,但不知怎么使用它。

这是我的过滤功能供您查看:

this.filterText = () => {
  //check if we have text in Input text area
  if (this.state.inputText) {
    let regEx = /\{[a-zA-Z_][a-zA-Z0-9_]*\}/;
    let inputText = this.state.inputText;
    this.state.data.forEach((token, index) => {
      let match = inputText.match(regEx);
      if (match) {
        console.log('match:', match);
        //should replace matched text with replacement text
        inputText.replace(regEx, this.state.data[index].replacementText);
      }
    });
  }
}

【问题讨论】:

    标签: javascript regex


    【解决方案1】:

    这是一个简单的 vanilla js 解决方案。例如,尝试在 textarea 中的任何位置键入 I like to {make} stuffs{make} 应替换为 create

    编辑

    已进行更改以支持递归替换。

    现在可以在替换字符串中包含一些 {token}

    代码还可以防止循环调用。

    尝试输入{hello}{circular} 以确保它按您想要的方式工作。

    片段

    document.addEventListener("DOMContentLoaded", function() {
      buildResult();
    });
    
    let tokens = {
      "civility": "Mr",
      "dummy": "really dummy",
      "foo": "bar",
      "firstName": "Marty",
      "lastName": "McFly",
      "hello": "Hello {firstName} {lastName}",
      "circular": "Hello {circular} {firstName} {lastName}",
      "make": "create",
    
    }
    
    function buildResult() {
      let text = document.getElementById('userInput').value;
      let result = replaceTokens(text);
      document.getElementById('result').innerHTML = result;
    }
    
    function replaceTokens(text, replacementStack) {
      // const re = /{([^}]+)}/g; // match anything but a `}` between braces
      const re = /{[\w]*\}/g; // match initial regex
    
      let result = text;
      let textTokens = text.match(re);
      replacementStack = replacementStack || [];
    
      textTokens && textTokens.forEach(m => {
        let token = m.replace(/{|}/g, '');
        // Prevent circular replacement, token should not have already replaced
        if (replacementStack.indexOf(token) === -1) {
          // add token to replacement stack
          replacementStack.push(token);
          let replacement = tokens[token];
          if (replacement) {
            replacement = replaceTokens(replacement, replacementStack);
            result = result.replace(m, replacement);
          }
        }
      });
    
      return result;
    }
    <!DOCTYPE html>
    <html>
    <head>
      <title></title>
      <script src="script.js"></script>
      <style>
        label { display: block; font-weight: bold; }
        textarea { width: 600px; height: 150px; }
      </style>
    </head>
    <body>
    
      <label>Enter text</label>
      <textarea id="userInput" onkeyup="buildResult()">Lorem Ipsum is simply {dummy} text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to {make} a type specimen book.
    It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</textarea>
    
      <label>Result</label>
      <div id="result"></div>
    
    </body>
    </html>

    【讨论】:

    • 我喜欢你的解决方案,通过一些调整我可以使我自己的示例工作。我有一个问题,但我的正则表达式和你的有什么区别吗?让这个例子也递归工作会很困难吗?如果我在替换文本中有 {test} ,那么我将再次解析它并检查 test 是否在令牌列表中并在文本中替换它。谢谢
    • 完成!我认为它可以满足您的需求。
    • 关于你的正则表达式,我只匹配大括号之间的任何东西,但 } 但如果你只需要匹配字母、数字或下划线,那么你可以像这样简化你的:{[\w]*\}\w 匹配任何字母、数字或下划线。等效于 [a-zA-Z0-9_]。大括号不需要转义。
    • 非常感谢,我不能要求更多了
    【解决方案2】:

    您发布的代码从未分配replace 的结果。我不确定data 结构,但以下应该与布局有些对应。

    function foo(){
    	this.state = {inputText:'this is a test that should {verb} both the first {noun} between {} and also the following {noun}s, but not {other} tags'};
      this.data = {verb:'replace', noun:'element'};
      this.filterText = () => !this.state.inputText || Object.keys(this.data).reduce((t,k)=>
      	t.replace(new RegExp('{' + k + '}','g'),this.data[k])
      	,this.state.inputText
      );
    }
    
    let f = new foo();console.log(f.filterText());

    想法是颠倒逻辑,不是找到所有的 {} 标签,而是使用标记作为来源(这与我现在看到的 Klaus 的回答相同)。 这里甚至不需要正则表达式来替换,但它用于global 标志

    【讨论】:

      【解决方案3】:

      它不起作用,因为替换函数中的正则表达式必须正是您要替换的内容(在这种情况下是令牌本身)。

      试试这个: https://jsfiddle.net/KlaussU/f4mxa3vw/2/

      <button onclick="replaceText('tokenX replaced: {tokenX}')">Try it</button>
      <p id="demo"></p>
      
      <script>
      var replacementText = {
          "tokenX": "tokenXReplaced",
          "tokenY": "tokenYReplaced",
          "tokenZ": "tokenZReplaced"
      };
      
      function replaceText(text) {
          for(var rt in replacementText) {
              text = text.replace(new RegExp("{" + rt + "}"), replacementText[rt]);
          }
          document.getElementById("demo").innerHTML = text;
      }
      </script>
      

      P.S 您可能会发现此处接受的答案也很有用: Javascript Regex: How to put a variable inside a regular expression?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-12-04
        • 2013-05-29
        • 2013-03-19
        • 1970-01-01
        • 2018-03-16
        • 1970-01-01
        • 2017-10-18
        相关资源
        最近更新 更多