解析Java中的复杂标志

时间:2019-04-30 13:47:37

标签: java parsing command-line-interface

我正在用Java将特定标志解析到我的CLI中。该标志是一个“流”标志,用--stN=表示,其中N代表一个从1到10000的数字。

=之后的参数用逗号分隔,并表示不同的内容-通常是有关如何在程序中运行第N次的特定命令。

这些命令本身可以具有0..N个参数。

--st1=command1,arg1,arg2,command2,arg2,3,command3,command4,5

它们可以是字符串,数字等的组合。

这是我建议的解决方案:

首先,我添加了一个名为StreamOption的接口来保存已解析的数据:

public interface StreamOption {
}

接下来,我创建了一个StreamOptionParser来处理流的一部分:

public interface StreamOptionParser<T extends StreamOption> {
    StreamReturn parse(List<String> stream, int id) throws ParameterException;
}

在实现中,我将流(作为列表,用逗号分隔)。

例如

public class CommandOneParser implements StreamOptionParser<CommandOneOption> {

 StreamReturn parse(List<String> stream, int id) throws ParameterException{
  //loop through the list, if terms are found, parse to a StreamObject
  //if not, pass to the return list of strings..
 }
}

结果类型StreamReturn是一个简单的POJO,它允许返回已解析的StreamOption以及需要传递给下一个解析器的所有剩余项。我还需要返回其余的术语,以检查所有输入是否有效,并且没有剩余。

//bit of lombok
@Getter
@Setter
@AllArgsConstructor
public class StreamReturn<T extends StreamOption> {

    private List<String> remainingOptions;
    private Optional<List<T>> options = Optional.empty();

}

它必须保留List,因为可以在同一流中指定多个命令。--stN=command1,2,3,command1,4,5,...

这是一个好方法吗?还是有一个更好的解决方案,减少了锅炉的麻烦?这有潜在的问题吗?我认为它保持了可扩展性,并且随着程序的增长,以后我可以轻松添加新的OptionsParsers

我似乎找不到开箱即用的此类解析库/模式,有没有我可能想念的东西?我将JCommander用于简单的标志,但这对于jcommander来说似乎太复杂了。

预先感谢, 山姆

2 个答案:

答案 0 :(得分:0)

因此, 的答案是通过分割字符串将其视为常规的CLI参数。然后,我利用JCommander进行所有耗时的工作,将正确的命令推入正确的转换器。

command1,2,3,command2,3,string1,command3

进入

command1
2,3
command2
3,string1
command3

有了这个,我利用JCommander批注将正确的args传递给正确的命令。

CommandOneValues{

  int myValue1;
  int myValue2;

}

和选项:

@Getter
public class CommandOneOptions {


    @Parameter(names = {"command1"},
        converter = CommandOneConverter.class
    )
    CommandOneValues values;
}

具有一个简单的转换器:

class CommandOneConverter implements IStringConverter<CommandOneValues>{

  public CommandOneValues convert(String value){
    //conversion...
  }

}

希望这对以后的人有帮助!

答案 1 :(得分:0)

其他工具可能具有一些功能,这些功能除了可以提供自定义类型转换器之外,还可以为其他解决方案提供思路。 例如,picocli具有一些可能有趣的复杂选项机制:

引用的选项

假设您对选项的定义如下:

@Option(names = "--st", split = ",")
List<String> parts;

默认情况下,picocli的split函数在引号内出现时会忽略split regex,因此您可以使用引号对参数进行分组。用户输入示例:

--st1="command1,arg1,arg2","command2,arg2,3","command3","command4,5"

Picocli将这些值分成以下几个部分:

"command1,arg1,arg2"
"command2,arg2,3"
"command3"
"command4,5"

以上是拆分的默认行为,不需要自定义转换器或任何其他自定义代码。

参数组

Picocli 4.0将支持重复的参数组(仍处于beta中),这可能是对问题进行建模的另一种方式。 如果应用程序的概要看起来像这样,对最终用户不好吗?

myapp (--cmd=<commandName> [<commandArg>...])...

也就是说,--cmd选项与零个或多个位置参数一起成为参数组,并且可以为该参数组指定一次或多次。 最终用户将能够输入如下值:

myapp --cmd=command1 arg1 arg2 --cmd=command2 arg2 3 --cmd=command3 --cmd=command4 5

完成此操作的代码如下:

@Command(name = "myapp", mixinStandardHelpOptions = true)
class MyApp implements Callable<Integer> {

    static class CommandDefinition {
        @Option(names = "--cmd", required = true) String commandName;
        @Parameters(index = "0..*")               List<String> commandArgs;
    }

    @ArgGroup(exclusive = false, multiplicity = "1..*")
    List<CommandDefinition> commands = new ArrayList<>();

    public Integer call() throws Exception {
        // your business logic here
        return 0;
    }

    public static void main(String... args) {
        int exitCode = new CommandLine(new MyApp()).execute(args);
        System.exit(exitCode);
    }
}