捕获引号中的字符串作为单个命令参数
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);
*/
我正在尝试制作一个与服务器进行一些交互的 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-stringC:\>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);
*/