如何以与bash相同的方式解析命令行序列?

How to parse a sequence of commands line in the same way bash would?

提问人:Madara's Ghost 提问时间:7/26/2015 更新时间:7/26/2015 访问量:70

问:

输入

我有以下示例输入(每个都是bash可执行命令):

client-properties create mode "publisher" "version" "mode"
client-properties set "publisher" "version" "mode" "prop1" "value
value
value"
client-properties set "publisher" "version" "mo\"de" "prop2" "שלום עליכם"

输出

由此,我想将其解析为 3 秒,如下所示:String[]

{"client-properties", "create", "mode", "publisher", "version", "mode"}
{"client-properties", "set", "publisher", "version", "mode", "prop1", "value\nvalue\nvalue"}
{"client-properties", "set", "publisher", "version", "mo\"de", "prop2", "שלום עליכם"}
//                                                   (mo"de)

要求

硬性要求如下:

  • 换行符表示新语句。
  • 空格表示单个参数。
  • 用双引号 () 分隔的参数被视为单个参数,即使它有空格或换行符"
  • 转义序列可用于在参数中插入文字双引号\"
  • 允许使用 Unicode 字符(假设 UTF8 是安全的)。

我试过什么

我研究过正则表达式,但它变得非常复杂,非常快。我研究了 StringTokenizer(看起来非常原始)和 StreamTokenizer(不能很好地处理 unicode )。

如果可能的话,我想避免手动编写解析器。

对此有什么想法吗?我的最新尝试如下:

public static List<String> tokenize(String s) {
    List<String> opts = new ArrayList<>();
    try (StringReader sr = new StringReader(s)) {
        StreamTokenizer st = new StreamTokenizer(sr);
        st.resetSyntax();
        // From      ! to end of ascii range. But alas, no unicode
        st.wordChars(31, 127);
        st.quoteChar('\"');
        st.whitespaceChars(32, 32);
        while (st.nextToken() != StreamTokenizer.TT_EOF) {
            opts.add(st.sval);
        }
    } catch (IOException e) {}

    return opts;
}
Java 字符串 解析 tokenize

评论

0赞 Marko Topolnik 7/26/2015
您将需要自定义解析。您非常接近 CSV 格式,因此您可能会找到一个与您的规范相匹配的公共 CSV 库。我最担心的是逃避引用。
0赞 Madara's Ghost 7/26/2015
@MarkoTopolnik:不完全是。我没有预定数量的列或标题。
0赞 Marko Topolnik 7/26/2015
您是否已经研究过 CSV 库?我不记得有谁需要预定数量的列。这通常是一个附加选项。
0赞 Pshemo 7/26/2015
应该被视为六个单独的字符,还是一个字符在Unicode表中的索引在哪里?\uHEXA\uHEXAHEXA
0赞 Madara's Ghost 7/26/2015
六个独立的字符。其他人解析实际参数并用它们做一些事情,但这被传递到 CommandLineParser 中(想想,所以它们需要在输入时传递。(当然,这将在输入文件中显示为Java字符串,main (String ...args)\uHEXA"\\uHEXA"

答:

2赞 Birei 7/26/2015 #1

您可以尝试使用库,使用以下方法导入:opencsvgradle

compile 'net.sf.opencsv:opencsv:3.4'

尝试类似于以下程序:

import com.opencsv.CSVReader;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {

    private static final char SEPARATOR = ' ';
    private static final char QUOTE_CHAR = '"';
    private static final char ESCAPE = '\\';

    public static void main(String[] args) throws IOException {
        List<String[]> result = new ArrayList<>();

        CSVReader reader = new CSVReader(
                new FileReader(args[0]),
                SEPARATOR,
                QUOTE_CHAR,
                ESCAPE);

        result.addAll(reader.readAll());

        for (int i = 0; i < result.size(); i++) {
            System.out.println(Arrays.toString(result.get(i)));
        }

    }

}

这产生了:

[client-properties, create, mode, publisher, version, mode]
[client-properties, set, publisher, version, mode, prop1, value
value
value]
[client-properties, set, publisher, version, mo"de, prop2, שלום עליכם]

评论

0赞 Madara's Ghost 7/26/2015
这实际上看起来很有前途!谢谢!我会看看我是否可以整合这个想法,如果我这样做,我会接受。