0

I'm making a TeamSpeak3 ServerQuery bot, command line style. I already have the commands down, but what I can't seem to wrap my head around is the arguments to a command. I use a reset() method to create a List of arguments so combining the string(s) would be easier.

For example, say I change a setting in memory for my bot name

set query name "Kyles Bot"

But the program takes "Kyles and Bot" as two different arguments. I want them as one. How would I go about doing this?

Fields needed for reset():

// Keep String[] and 3 strings null for now, they'll be changed.
private String command, to1, to2;
private String[] to3;
private List<String> args = new ArrayList<>();

The reset() method:

private void reset() {
            args.clear();
            to1 = line.getText();
            command = to1.split(" ")[0];
            if (to1.split(" ").length > 1) {
                to2 = to1.substring(command.length() + 1, to1.length());
                to3 = to2.split(" ");
                for (int i = 0; i < to3.length; i++) {
                    if (to3[i].isEmpty() || to3[i].startsWith(" ")) {
                        System.out.println("Argument null, command cancelled. [" + to3[i] + "]");
                        break;
                    } else {
                        args.add(to3[i]);
                    }
                }
                //EDIT2: Removed useless for loop, 
                //it was my previous attempt to solve the problem.
            } else {
                //EDIT: This loop here is used to prevent AIOUB
                command = to1;
                for (int i = 0; i < 5; i++) {
                    args.add("NullElement");
                }
            }
        }
FlashDaggerX
  • 89
  • 10
  • `set query name "Kyles Bot"` this looks suspicious. Are you getting an environment variable? They can't contain spaces. Try `set queryname "Kyles Bot"` – Michael Jul 07 '17 at 12:27
  • No @Michael , the commands are interacting with a HashMap I have in another class. Sorry if that wasn't clear. I'll post the class in an edit if you'd like. – FlashDaggerX Jul 07 '17 at 12:29
  • This is already quite a long code sample so don't do that. I suspect a large majority of your code isn't related to this specific problem. Please create a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) - basically remove *everything* that isn't relevant, and make sure the code you post is able to run on its own if we copy and paste it to an IDE. If you do that, I will make sure to either solve your problem or raise a bounty so someone else will. – Michael Jul 07 '17 at 12:35
  • Only the reset() method and fields used are pasted into the question - Thanks for the head up! – FlashDaggerX Jul 07 '17 at 12:41
  • 1
    Possible duplicate of [Tokenizing a String but ignoring delimiters within quotes](https://stackoverflow.com/questions/3366281/tokenizing-a-string-but-ignoring-delimiters-within-quotes) – slim Jul 07 '17 at 12:54

1 Answers1

1

The problem is this line:

to3 = to2.split(" ");

it splits the read command on every space, including spaces inside quoted text.

You need to split the command line properly, for example using a Regular Expression:

    // matches either a "quoted string" or a single word, both followed by any amount of whitespace
    Pattern argumentPattern = Pattern.compile("(\"([^\"]|\\\")*\"|\\S+)\\s*");

    // loop over all arguments
    Matcher m = argumentPattern.matcher(to2);
    for (int start = 0; m.find(start); start = m.end()) {

        // get a single argument and remove whitespace around it
        String argument = m.group(1).trim();

        // handle quoted arguments - remove outer quotes and unescape inner ones
        if (argument.startsWith("\""))
            argument = argument.substring(1,  argument.length() - 1).replace("\\\"", "\"");

        // ... your code that uses the argument here

    }

Please note that this is not a complete implementation of a command line parser – if you receive arbitrary commands, you should look into libraries that do this parsing for you and can handle all the specifics properly.

PS: please use descriptive variable names instead of to1, to2, to3 etc., for example I used argument instead of to3 in my code.

Njol
  • 3,271
  • 17
  • 32
  • I didn't even know Pattern existed, thanks for the suggestion! I'll test it in a second – FlashDaggerX Jul 07 '17 at 13:01
  • @KyleTheHacker I just fixed a bug in the code, you'll need to use the new version for testing (the loop and its first line are different now, the rest is the same) – Njol Jul 07 '17 at 13:21