使用引号捕获字符串作为单个命令参数

时间:2018-06-03 22:44:53

标签: node.js discord.js

我试图制作与服务器进行某些互动的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 个答案:

答案 0 :(得分: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上试用此代码!

答案 1 :(得分:1)

我遇到了这个问题,因为我对OP有类似的要求(解析一个可能包含带双引号的参数且带有嵌入式空格的字符串)。但是,被接受的答案并不能满足我的需要(它会删除空格,并过多地考虑了参数的数量)。因此,我不得不设计自己的解决方案,以防其他人发现它有用。

实际上有两种变体:第一种是 not 不允许双引号出现在生成的参数列表中;第二个确实通过在双引号字符串内使用双双引号(...""...)来实现此目的。 (我实际上首先写了这个版本,“因为这是Windows下Node处理事情的方式”,然后将其缩减为第一个变体。

在两个示例中,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下的Node如何解析命令行(未经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' ]

答案 2 :(得分:0)

您可以添加更明确的分隔符,例如&#34; |&#34;并使用拆分(&#39; |&#39;)

您的输入将是:!file GrumpyCrouton | &#34;球员的时间不好&#34; |踢| &#34; 1天&#34;

答案 3 :(得分:0)

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

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