【问题标题】:shlex alternative for JavaJava的shlex替代品
【发布时间】:2010-11-08 03:37:48
【问题描述】:

是否有 Java 的 shlex 替代方案?我希望能够像 shell 处理它们一样拆分引号分隔的字符串。例如,如果我发送:

一二“三四”
并进行拆分,我想收到令牌


三四

【问题讨论】:

  • 值得注意的是——“就像shell会处理它们一样”是一项相当艰巨的任务; shlex 做得很好,但许多幼稚的算法不会。例如,在 shell 中,"three four""three"' 'four 是完全等价的,three\ four 也是如此。

标签: java bash shell tokenize


【解决方案1】:

我今天遇到了类似的问题,它看起来不像任何标准选项,如 StringTokenizer、StrTokenizer、Scanner 都适合。但是,实现基础并不难。

此示例处理当前在其他答案中评论的所有边缘情况。请注意,我还没有检查它是否完全符合 POSIX。 Gist 包括单元测试可用 on GitHub - 通过未经许可在公共领域发布。

public List<String> shellSplit(CharSequence string) {
    List<String> tokens = new ArrayList<String>();
    boolean escaping = false;
    char quoteChar = ' ';
    boolean quoting = false;
    int lastCloseQuoteIndex = Integer.MIN_VALUE;
    StringBuilder current = new StringBuilder();
    for (int i = 0; i<string.length(); i++) {
        char c = string.charAt(i);
        if (escaping) {
            current.append(c);
            escaping = false;
        } else if (c == '\\' && !(quoting && quoteChar == '\'')) {
            escaping = true;
        } else if (quoting && c == quoteChar) {
            quoting = false;
            lastCloseQuoteIndex = i;
        } else if (!quoting && (c == '\'' || c == '"')) {
            quoting = true;
            quoteChar = c;
        } else if (!quoting && Character.isWhitespace(c)) {
            if (current.length() > 0 || lastCloseQuoteIndex == (i - 1)) {
                tokens.add(current.toString());
                current = new StringBuilder();
            }
        } else {
            current.append(c);
        }
    }
    if (current.length() > 0 || lastCloseQuoteIndex == (string.length() - 1)) {
        tokens.add(current.toString());
    }

    return tokens;
}

【讨论】:

  • 您是否会考虑为此附上许可证(或明确将其捐赠给公共领域)?
  • 啊,就是这样,本页的最后一行:在 cc by-sa 3.0 下获得许可的用户贡献,需要注明出处
  • @RayMyers:我们仍然需要知道这是否是你自己的作品,否则许可证是未知的。此外,CC-BY-SA 许可证与 Hadoop 的 Apache 许可证 (I would need to use it unmodified) 不完全兼容。如果您将此代码专用于the Unlicense,这些问题就会消失,否则我将不得不从头开始编写类似的代码。 ...我希望 SO 更改他们的默认许可证。
  • bukzor 和其他人:感谢您指出这一点。是的,这是我的工作。我已将其更新为明确的公共领域。
  • 当心:此代码不正确地处理带引号的空字符串。例如输入 "''" 将被解析为一个空列表,而不是包含 "" 的列表。
【解决方案2】:

Apache Commons Lang

org.apache.commons.lang.text.StrTokenizer 应该能够做你想做的事:

new StringTokenizer("一二\"三四\"", '', '"').getTokenArray();

【讨论】:

  • 不幸的是,与shlex 不同,commons.lang 不兼容 POSIX。 (-&gt; (StrTokenizer. "\"foo\"'bar'baz") (.getTokenList)) 返回一个包含 "foo"'bar'baz 的条目,而不是(正确的)foobarbaz
  • @CharlesDuffy 你知道真正的答案吗?
  • @bukzor,假设存在 一个。据我所知,目前还没有编写这样的工具,没有通过 Jython 使用来自 Java 的 Python shlex(可能,但需要引入一个大的依赖链)。
  • ...虽然@RayMyers 的答案看起来像是一个可能的候选人。
【解决方案3】:

我使用fastparse 成功使用了以下 Scala 代码。我不能保证它是完整的:

val kvParser = {
  import fastparse._
  import NoWhitespace._
  def nonQuoteChar[_:P] = P(CharPred(_ != '"'))
  def quotedQuote[_:P] = P("\\\"")
  def quotedElement[_:P] = P(nonQuoteChar | quotedQuote)
  def quotedContent[_:P] = P(quotedElement.rep)
  def quotedString[_:P] = P("\"" ~/ quotedContent.! ~ "\"")
  def alpha[_:P] = P(CharIn("a-zA-Z"))
  def digit[_:P] = P(CharIn("0-9"))
  def hyphen[_:P] = P("-")
  def underscore[_:P] = P("_")
  def bareStringChar[_:P] = P(alpha | digit | hyphen | underscore)
  def bareString[_:P] = P(bareStringChar.rep.!)
  def string[_:P] = P(quotedString | bareString)
  def kvPair[_:P] = P(string ~ "=" ~ string)
  def commaAndSpace[_:P] = P(CharIn(" \t\n\r").rep ~ "," ~ CharIn(" \t\n\r").rep)
  def kvPairList[_:P] = P(kvPair.rep(sep = commaAndSpace))
  def fullLang[_:P] = P(kvPairList ~ End)

  def res(str: String) = {
    parse(str, fullLang(_))
  }

  res _
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    • 2012-02-28
    • 1970-01-01
    • 2016-02-29
    • 2012-10-24
    • 1970-01-01
    相关资源
    最近更新 更多