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

Capture strings in quotes as single command argument

我正在尝试制作一个与服务器进行一些交互的 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} 可能类似于 "Player had a bad time",但由于参数的拆分方式,我的代码无法正确解析它。

假设输入了这个命令:

!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();

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

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

您的输入如下:!file GrumpyCrouton | "Player had a bad time" |踢球 | “1 天”

您可以找到引号的所有索引,并使用该信息通过将输入传递给 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!

上试用此代码

我提出这个问题是因为我对 OP 有类似的要求(解析可能包含 double-quoted 参数的字符串,其中嵌入了 spaces)。然而,接受的答案并没有满足我的需要(它去掉了 spaces,并且对参数的数量假设太多)。因此,我不得不设计我自己的解决方案,我在这里提供,以防其他人发现它有用。

实际上有两种变体:第一种允许double-quotes出现在生成的参数列表中;第二个 通过在 double-quoted 字符串中使用 double-double-quote (...""...) 允许这样做。 (我实际上首先写了这个版本“因为那是 Windows 下的 Node 做事的方式”,然后将其删减为第一个变体。

在这两个示例中,log() 函数以及从 splitCommandLine() 中调用它纯粹是为了展示内部工作原理,可以省略。


简单Double-Quoted字符串

  • 通常会在 space 秒拆分参数。
  • Double-quoted 字符串被视为一个参数,即使它们包含 spaces.
  • spacedouble-quotes内的多个
  • 将被保留。
  • 多个space之外double-quotes它们将被视为单个space。
  • 如果缺少最终的结束语 double-quote,将假定它。
  • 无法将 double-quote 字符放入参数中。
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 ;
}

运行 这与代码中嵌入的相同 command-line 表明它产生与 Node/Windows command-line 解析器相同的输出:

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' ]

Double-Quoted 带 Double-Double-Quotes

的字符串
  • 与第一个示例完全相同,除了在double-quoted字符串中,double-double-quote(..."aaa ""bbb"" ccc"...)将插入一个double-quote 中的 parsed-parameter (aaa "bbb" ccc)。在 double-quoted 字符串之外,double-double-quote 将被忽略。这模仿了 Windows 下的节点如何解析 command-line(未在 Unix-variants 上测试)。
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:

相同的方式解析 command-string
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' ]

一个简单的正则表达式就可以解决问题:)

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);
 */