【问题标题】:How to split string into arguments and options in javascript如何在javascript中将字符串拆分为参数和选项
【发布时间】:2012-12-10 07:20:21
【问题描述】:

我想获取参数数组,这样我就可以将它与optparse-js library 一起使用,所以如果我有类似的东西

-f foo -b -a -z baz bar

我想要这样的数组

["-f", "foo", "-b", "-a", "-z", "baz", "bar"]

它应该适用于内部有转义引号和长 GNU 选项的字符串。到目前为止,我有匹配字符串的正则表达式

/("(?:\\"|[^"])*"|'(?:\\'|[^'])*')/g

它匹配像"das""asd\"asd"'asd''sad\'asd'这样的字符串

我可以为此使用正则表达式还是我需要一个解析器(例如使用 PEG)如果它匹配正则表达式会很好,这样我就可以做到

-p "hello b\"ar baz" -f /^ [^ ]+ $/

更新:在@Damask 的帮助下,我创建了这个正则表达式:

/('(\\'|[^'])*'|"(\\"|[^"])*"|\/(\\\/|[^\/])*\/|(\\ |[^ ])+|[\w-]+)/g

它适用于这样的字符串:

echo -p "hello b\"ar baz" -f /^ [^ ]+ $/

返回

['echo', '-p', '"hello b\"ar baz"', '-f', '/^ [^ ]+ $/']

但如果在这样的字符串上失败:

echo "©\\\\" abc "baz"

它匹配命令和两个参数而不是三个参数demo

如果参数没有像 "foo"baz 这样的空格,它应该是数组中的一项,需要包含引号,但我会从字符串中删除未转义的那些(就像在 bash 中执行 echo "foo"bar echo 时一样一个 foobar 参数)。

【问题讨论】:

  • 要从第一个字符串到提到的数组,你可以使用 split(" ") 但我假设你需要详细说明前两句 ( ̄(エ) ̄)
  • @mplungjan 我需要可以与-p "hello b\"ar baz" -f /^ [^ ]+ $/类似的解决方案
  • 所以我建议你交换你的例子,并用一个真实的例子来展示数组的样子
  • 输入是否为“-f foo -b -a -z baz bar”是否为字符串

标签: javascript regex


【解决方案1】:

一些cmets:

  • 引号的原始正则表达式是这样的
    "[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'
    示例:http://regex101.com/r/uxqApc/2

  • 这部分(?= :? | $ )会一直解析为真,没用

  • 这部分 /(\\/|[^/])+/[gimy]* 如果这是一个正则表达式(或任何分隔项)
    你必须盲目地处理逃避任何事情。喜欢这个/[^/\\]*(?:\\[\S\s][^/\\]*)*/[gimy]*
    否则它将匹配不正确的/..\\//

  • 这个表达式(?: \\ \s | \S )+在交替序列中是第一个,即在这个[\w-]+之前。由于不是空格\S[\w-] 的超集,这意味着这个[\w-]+ 永远不会到达。

进行更正并将所有内容重新组合在一起得到这个正则表达式:
/("[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|\/[^\/\\]*(?:\\[\S\s][^\/\\]*)*\/[gimy]*(?=\s|$)|(?:\\\s|\S)+)/

演示:

JavaScript - http://regex101.com/r/cuJuQ8/1
PCRE - http://regex101.com/r/cuJuQ8/2

Formatted

 (                             # (1 start)
      "
      [^"\\]* 
      (?: \\ [\S\s] [^"\\]* )*
      "
   |  
      ' 
      [^'\\]* 
      (?: \\ [\S\s] [^'\\]* )*
      '
   |  
      / 
      [^/\\]* 
      (?: \\ [\S\s] [^/\\]* )*
      /
      [gimy]* 
      (?= \s | $ )
   |  
      (?: \\ \s | \S )+
 )                             # (1 end)


如果你也需要像空格(引号或正则表达式之外)也是分隔符一样解析它,那就是它:

/((?:"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|\/[^\/\\]*(?:\\[\S\s][^\/\\]*)*\/[gimy]*(?=\s|$)|(?:\\\s|\S))+)(?=\s|$)/

演示:

JavaScript - http://regex101.com/r/cuJuQ8/3
PCRE - https://regex101.com/r/cuJuQ8/4

格式化

 (                             # (1 start)
      (?:
           "
           [^"\\]* 
           (?: \\ [\S\s] [^"\\]* )*
           "
        |  
           ' 
           [^'\\]* 
           (?: \\ [\S\s] [^'\\]* )*
           '
        |  
           / 
           [^/\\]* 
           (?: \\ [\S\s] [^/\\]* )*
           /
           [gimy]* 
           (?= \s | $ )
        |  
           (?: \\ \s | \S )
      )+
 )                             # (1 end)
 (?= \s | $ )

【讨论】:

    【解决方案2】:

    我真的很喜欢正则表达式,但有时简单的正则表达式和简单函数的组合可以完成相同的工作,但更容易调试和维护,尤其是当不熟悉 (complex) 正则表达式的开发人员加入项目。

    所以这是另一种方法,请参阅下面的说明。

    使用这个相当复杂的示例进行测试,其中包含许多空格或根据需要转义双引号的参数:

    echo "©\\\\" abc "baz" "foo bar dummy" -d "marty \\\"mc fly" -f "avb eer\"" -p 2 "asd\"asd" -a 3

    代码片段

    function commandArgs2Array(text) {
      const re = /^"[^"]*"$/; // Check if argument is surrounded with double-quotes
      const re2 = /^([^"]|[^"].*?[^"])$/; // Check if argument is NOT surrounded with double-quotes
    
      let arr = [];
      let argPart = null;
    
      text && text.split(" ").forEach(function(arg) {
        if ((re.test(arg) || re2.test(arg)) && !argPart) {
          arr.push(arg);
        } else {
          argPart = argPart ? argPart + " " + arg : arg;
          // If part is complete (ends with a double quote), we can add it to the array
          if (/"$/.test(argPart)) {
            arr.push(argPart);
            argPart = null;
          }
        }
      });
    
      return arr;
    }
    
    let result = commandArgs2Array('echo "©\\\\" abc "baz" "foo bar  dummy" -d "marty \\\"mc fly" -f "avb eer\"" -p 2 "asd\"asd" -a 3');
    console.log(result);

    说明

    首先,使用空格字符分割参数。

    对于每个参数,我们检查它是 complete 还是 incomplete 参数

    一个 complete 参数是一个参数,它要么是

    • 用双引号括起来
    • 根本不用双引号

    所有其他情况都代表一个不完整参数。要么是

    • 不完整参数的开头(以双引号开头)
    • 一个空间
    • 不完整参数的一部分,可以包含转义的双引号
    • 不完整参数的结尾(以双引号结尾)

    这就是所有人!

    【讨论】:

    • 错误:{“消息”:“语法错误”,“文件名”:“stacksnippets.net/js”,“lineno”:20,“colno”:40 }
    • 这是因为您使用的浏览器不支持 ES2015。我已经编辑了答案来修复它。谢谢指点。
    【解决方案3】:

    你为什么不简单地使用拆分功能?

    var arr = myString.split(/\s+/);
    

    最好将正则表达式作为参数传递,以避免在分隔符为 \t 或有多个空格等情况下出现错误。

    编辑:

    如果你的参数有空格并且用引号引起来,我认为你找不到一个正则表达式。认为你应该首先找到带空格的参数(/"(.*?)"/ 在第 1 组中你会得到参数),将它们添加到数组中,然后从字符串中删除它们,然后才使用上述拆分方法。

    【讨论】:

    • 我也建议过。但这似乎太简单了,无法给出答案
    • 我不能只使用它,因为我可以将带有空格的字符串或正则表达式作为选项的参数
    • IIRC,有一个解决方案来标记引号内的字符串 ",但它需要 JS 正则表达式中不可用的功能。
    【解决方案4】:

    试试这个:

    var a = '-f foo "ds  df s\\" da" -b -a -z baz bar';
    a.match(/([\w-]+|"(\\"|[^"])*")/g)
    

    返回[ "-f", "foo", ""ds df s\" da"", "-b", "-a", "-z", "baz", "bar"]

    【讨论】:

    • 在您的帮助下,我创建了更好的正则表达式 /('(\\'|[^'])*'|"(\\"|[^"])*"|\/(\\\/|[^\/])*\/|(\\ |[^ ])+|[\w-]+)/g,它可以匹配正则表达式、单引号和带有转义空格的文本。
    【解决方案5】:

    这将起作用:

    var input = '-p "hello b\"ar baz" -f /^ [^ ]+ $/ -c -d -e'
    var arr = input.split(' -');
    var out = [];
    for(var i = 0; i < arr.length; i++){
        if(~arr[i].indexOf(' ')){
            out = out.concat([arr[i].substring(0, arr[i].indexOf(' ')), arr[i].substring(arr[i].indexOf(' ')+1)])
        }else{
            out = out.concat('-'+arr[i]);
        }
    }
    

    输出:

    ["-p", ""hello b"ar baz"", "f", "/^ [^ ]+ $/", "-c", "-d", "-e"]
    

    我知道这不是一个花哨的 1 行正则表达式,但它可以像预期的那样工作。

    【讨论】:

    • 它不适用于转义引号 var input = 'echo "asd\\"asd" asd' 在您的情况下,转义 \" 应该是 \\" 以在输入中使用斜杠而不是引号。
    【解决方案6】:
     var string = "-f foo -b -a -z baz bar";
            string = string.split(" ");
        var stringArray = new Array();
        for(var i =0; i < string.length; i++){
            stringArray.push(string[i]);
        }
        console.log(stringArray);
    

    输出将是这样的控制台

    数组[“-f”、“foo”、“-b”、“-a”、“-z”、“baz”、“bar”]

    【讨论】:

    • 这不适用于 'echo "foo bar" baz` 并且你不需要遍历数组,字符串已经是一个数组,所以 stringArraystring 是一样的.
    【解决方案7】:

    这个问题的另一个选项:https://github.com/elgs/splitargs

    【讨论】:

    • 它不适用于奇数/偶数的转义引号。
    【解决方案8】:

    好的,即使我为这个问题创建了一个赏金,我还是在 Regex match even number of letters 的帮助下找到了答案

    我的正则表达式看起来像这样:

    /('((?:[^\\]*(?:\\\\)*\\')+|[^']*)*'|"(?:(?:[^\\]*(?:\\\\)*\\")+|[^"]*)*"|(?:\/(\\\/|[^\/])+\/[gimy]*)(?=:? |$)|(\\\s|\S)+|[\w-]+)/
    

    with demo

    编辑:@sin 建议制作更好的正则表达式:

    /("[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|(?:\/(\\\/|[^\/])+\/[gimy]*)(?=:? |$)|(\\\s|\S)+|[\w-]+)/
    

    【讨论】:

    • 这个也是错的,失败是因为你没有考虑到引号或斜线以外的字符也可能被转义,例如:"ab\cd"。您也不需要知道反斜杠的数量是奇数还是偶数。最后,使用[^\\]* (在开头) 允许走出引用的部分并到达最终的其他引用部分:'abc' -p 'def\'ghi'。将引用部分与转义引号匹配的一种简单方法是:'[^'\\]*(?:\\.[^'\\]*)*' (如果您还想匹配转义换行符,请将点替换为 [\s\S][^]
    • 另外,我不明白你为什么在模式部分后添加(?=:? |$)
    • 引号的原始正则表达式是 "[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*' (regex101.com/r/uxqApc/2) 你到底想做什么??
    • @sin 哦,谢谢,字符串匹配正则表达式要好得多我将它添加到我的命令行拆分正则表达式中,它还需要匹配普通的单词、数字和正则表达式。
    • 我不确定你的正则表达式的其他部分应该做什么,但这部分 (?= :? | $ ) 将始终解析为 true,并且没用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 2021-11-29
    • 2015-05-23
    • 2016-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多