代码之家  ›  专栏  ›  技术社区  ›  GrumpyCrouton

将引号中的字符串捕获为单个命令参数

  •  2
  • GrumpyCrouton  · 技术社区  · 6 年前

    我正在尝试制作一个Discord机器人,它可以与服务器进行一些交互。

    我已经写了一些类似的代码,但有一个大问题。这是我的代码:

    if (command === "file") {
    
            var accusor = message.author.id;
            var username = args[0];
            var reason = args[1];
            var punishment = args[2];
            var duration = args[3];
            if(!duration) duration = "N/A";
            console.log("Returning last " + amount + " for " + username);
            request.post({url:'http://grumpycrouton.com/kismet/api/post_complaint.php', form: {accusor:accusor,search:username,reason:reason,punishment:punishment,duration:duration}}, function(err,httpResponse,body) { 
                message.reply(body); 
            });
        }
    

    命令是 !file {playername} {reason} {punishment} {duration} ,但问题是,有时一些变量可能有多个单词。例如 {reason} 可能有点像“玩家玩得不开心”,但我的代码无法正确解析,因为参数是如何分割的。

    假设输入了以下命令:

    !file GrumpyCrouton "Player had a bad time" Kick "1 Day" 但是这些参数实际上会以不同的方式展开,因为第三个参数中有空格,但是正则表达式将所有参数按空格拆分,而不考虑引号。基本上,Discord会忽略引用,并将每个单词用作自己的论点,从而使 {punishment} {duration} 有一个参数索引是6和7,而不是2和3,因为每个单词都算作一个参数。

    我的论点是这样解读的:

    const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
    const command = args.shift().toLowerCase();
    

    如何使引号中的字符串作为单个参数而不是多个参数读取?

    4 回复  |  直到 6 年前
        1
  •  1
  •   TripeHound    5 年前

    一个简单的正则表达式就可以做到这一点:)

    const input = 'ban user "Look at what you have done!" 7d "This is another string" value';
    const regex = new RegExp('"[^"]+"|[\\S]+', 'g');
    const arguments = [];
    input.match(regex).forEach(element => {
        if (!element) return;
        return arguments.push(element.replace(/"/g, ''));
    });
    console.log(arguments);
    
    /*
     * Use a function with a spreader like:
     * doSomething(...arguments);
     */
    
        2
  •  1
  •   bluecereal    6 年前

    您可以找到引号的所有索引,并通过将其传递给 input.substring .像这样的方法应该有效:

    const input = '!file GrumpyCrouton \"Player had a bad time\" Kick \"1 Day\"';
    var raw = input;
    raw = raw.split(' ');
    let command = raw.splice(0, 1)[0]; // splice out the command (splice returns an array)
    let user = raw.splice(0, 1)[0];    // splice out the user
    
    let recompose = raw.join('');      // recompose the string WITHOUT any spaces
    
    let indices = []; // find the indices of the quotation marks
    for (var i in recompose) {
        let char = recompose[i];
      if (char === '"') {
        indices.push(i);
      }
    }
    
    console.log(indices, recompose);
    if (indices.length == 4) { // OK!
      // use the indices to break up input string into substrings
      let reason = recompose.substring(indices[0] + 1, indices[1]);
      let punishment = recompose.substring(indices[1], indices[2]).replace('"', '');
      let duration = recompose.substring(indices[2], indices[3]).replace('"', '');
      console.log(command);
      console.log(user);
      console.log(reason);
      console.log(punishment);
      console.log(duration);
    } else {
        // bad input!
    }
    

    你可以试试这段代码 jsfiddle !

        3
  •  0
  •   Matt Pengelly    6 年前

    我之所以要问这个问题,是因为我对OP有类似的要求(解析可能包含带嵌入空格的双引号参数的字符串)。然而,被接受的答案并没有满足我的需要(它去掉了空格,并对参数的数量做了太多假设)。因此,我必须设计自己的解决方案,我在这里提供,以防其他人发现它有用。

    实际上有两种变体:第一种变体 允许双引号出现在生成的参数列表中;第二个 允许使用双引号( ...""... )在双引号字符串中。(实际上是我先写的这个版本) 因为Windows下的节点就是这样做的 “然后在第一个变体中减少它。

    在这两个例子中 log() 函数,并从内部调用它 splitCommandLine() ,仅用于显示内部工作,可以省略。


    简单双引号字符串

    • 参数通常会在空间上拆分。
    • 双引号字符串被视为一个参数,即使它们包含空格。
    • 多个空间 在内部 双引号将被保留。
    • 多个空间 外部 双引号它们将被视为一个空格。
    • 如果缺少最终的收盘双引号,则将假定为该双引号。
    • 无法在参数中输入双引号字符。
    splitCommandLine( 'param1   "   param   2" param3 "param  4  " "param 5' ) ;
    
    log( 'argv', process.argv.slice(2) ) ;
    
    function log( n, v ) {
        console.log( n ) ;
        console.dir( v ) ;
        console.log() ;
    }
    
    function splitCommandLine( commandLine ) {
    
        log( 'commandLine', commandLine ) ;
    
        //  Find a unique marker for the space character.
        //  Start with '<SP>' and repeatedly append '@' if necessary to make it unique.
        var spaceMarker = '<SP>' ;
        while( commandLine.indexOf( spaceMarker ) > -1 ) spaceMarker += '@' ;
    
        //  Protect double-quoted strings.
        //   o  Find strings of non-double-quotes, wrapped in double-quotes.
        //   o  The final double-quote is optional to allow for an unterminated string.
        //   o  Replace each double-quoted-string with what's inside the qouble-quotes,
        //      after each space character has been replaced with the space-marker above.
        //   o  The outer double-quotes will not be present.
        var noSpacesInQuotes = commandLine.replace( /"([^"]*)"?/g, ( fullMatch, capture ) => {
            return capture.replace( / /g, spaceMarker ) ;
        }) ;
    
        log( 'noSpacesInQuotes', noSpacesInQuotes ) ;
    
        //  Now that it is safe to do so, split the command-line at one-or-more spaces.
        var mangledParamArray = noSpacesInQuotes.split( / +/ ) ;
    
        log( 'mangledParamArray', mangledParamArray ) ;
    
        //  Create a new array by restoring spaces from any space-markers.
        var paramArray = mangledParamArray.map( ( mangledParam ) => {
            return mangledParam.replace( RegExp( spaceMarker, 'g' ), ' ' ) ;
        });
    
        log( 'paramArray', paramArray ) ;
    
        return paramArray ;
    }
    

    使用与代码中嵌入的命令行相同的命令行运行此命令,表明它生成的输出与Node/Windows命令行解析器相同:

    C:\>node test1.js param1   "   param   2" param3 "param  4  " "param 5
    commandLine
    'param1   "   param   2" param3 "param  4  " "param 5'
    
    noSpacesInQuotes
    'param1   <SP><SP><SP>param<SP><SP><SP>2 param3 param<SP><SP>4<SP><SP> param<SP>5'
    
    mangledParamArray
    [ 'param1',
      '<SP><SP><SP>param<SP><SP><SP>2',
      'param3',
      'param<SP><SP>4<SP><SP>',
      'param<SP>5' ]
    
    paramArray
    [ 'param1', '   param   2', 'param3', 'param  4  ', 'param 5' ]
    
    argv
    [ 'param1', '   param   2', 'param3', 'param  4  ', 'param 5' ]
    

    带双引号的双引号字符串

    • 与第一个例子完全相同, 除了 在双引号字符串中,双引号( ..."aaa ""bbb"" ccc"... )将在解析的参数中插入双引号( aaa "bbb" ccc )。在双引号字符串之外,双引号将被忽略。这模拟了Windows下的节点解析命令行的方式(未在Unix变体上测试)。
    splitCommandLine( 'param1   "   param   2" param""3 "param "" 4  " "param 5' ) ;
    
    log( 'argv', process.argv.slice(2) ) ;
    
    function log( n, v ) {
        console.log( n ) ;
        console.dir( v ) ;
        console.log() ;
    }
    
    function splitCommandLine( commandLine ) {
    
        log( 'commandLine', commandLine ) ;
    
        //  Find a unique marker for pairs of double-quote characters.
        //  Start with '<DDQ>' and repeatedly append '@' if necessary to make it unique.
        var doubleDoubleQuote = '<DDQ>' ;
        while( commandLine.indexOf( doubleDoubleQuote ) > -1 ) doubleDoubleQuote += '@' ;
    
        //  Replace all pairs of double-quotes with above marker.
        var noDoubleDoubleQuotes = commandLine.replace( /""/g, doubleDoubleQuote ) ;
    
        log( 'noDoubleDoubleQuotes', noDoubleDoubleQuotes ) ;
    
        //  As above, find a unique marker for spaces.
        var spaceMarker = '<SP>' ;
        while( commandLine.indexOf( spaceMarker ) > -1 ) spaceMarker += '@' ;
    
        //  Protect double-quoted strings.
        //   o  Find strings of non-double-quotes, wrapped in double-quotes.
        //   o  The final double-quote is optional to allow for an unterminated string.
        //   o  Replace each double-quoted-string with what's inside the qouble-quotes,
        //      after each space character has been replaced with the space-marker above;
        //      and each double-double-quote marker has been replaced with a double-
        //      quote character.
        //   o  The outer double-quotes will not be present.
        var noSpacesInQuotes = noDoubleDoubleQuotes.replace( /"([^"]*)"?/g, ( fullMatch, capture ) => {
            return capture.replace( / /g, spaceMarker )
                          .replace( RegExp( doubleDoubleQuote, 'g' ), '"' ) ;
        }) ;
    
        log( 'noSpacesInQuotes', noSpacesInQuotes ) ;
    
        //  Now that it is safe to do so, split the command-line at one-or-more spaces.
        var mangledParamArray = noSpacesInQuotes.split( / +/ ) ;
    
        log( 'mangledParamArray', mangledParamArray ) ;
    
        //  Create a new array by restoring spaces from any space-markers. Also, any
        //  remaining double-double-quote markers must have been from OUTSIDE a double-
        //  quoted string and so are removed.
        var paramArray = mangledParamArray.map( ( mangledParam ) => {
            return mangledParam.replace( RegExp( spaceMarker,       'g' ), ' ' )
                               .replace( RegExp( doubleDoubleQuote, 'g' ), ''  ) ;
        });
    
        log( 'paramArray', paramArray ) ;
    
        return paramArray ;
    }
    

    同样,这段代码以与Node/Windows相同的方式解析命令字符串:

    C:\>node test2.js param1   "   param   2" param""3 "param "" 4  " "param 5
    commandLine
    'param1   "   param   2" param""3 "param "" 4  " "param 5'
    
    noDoubleDoubleQuotes
    'param1   "   param   2" param<DDQ>3 "param <DDQ> 4  " "param 5'
    
    noSpacesInQuotes
    'param1   <SP><SP><SP>param<SP><SP><SP>2 param<DDQ>3 param<SP>"<SP>4<SP><SP> param<SP>5'
    
    mangledParamArray
    [ 'param1',
      '<SP><SP><SP>param<SP><SP><SP>2',
      'param<DDQ>3',
      'param<SP>"<SP>4<SP><SP>',
      'param<SP>5' ]
    
    paramArray
    [ 'param1', '   param   2', 'param3', 'param " 4  ', 'param 5' ]
    
    argv
    [ 'param1', '   param   2', 'param3', 'param " 4  ', 'param 5' ]
    
        4
  •  0
  •   Matthew    5 年前

    您可以添加一个更明确的分隔符,如“|”,并使用split(“|”)

    你的输入应该是:!file GrumpyCrouton |“玩家玩得不开心”| Kick |“1天”