【问题标题】:Best way to retrieve a value from a string java从字符串java中检索值的最佳方法
【发布时间】:2011-08-18 18:09:19
【问题描述】:

如果我被传递一个包含逗号分隔键值对的字符串,像这样

seller=1000,country="canada",address="123 1st st"

似乎必须有比解析然后迭代更好的方法。

根据 Java 中的键名从该字符串中检索值的最佳方法是什么?

【问题讨论】:

  • 不,解析确实没有任何办法。您是否要求优化的解析算法?只需将整个 String 解析为 Map,您的意见是什么?
  • 将 String 解析为 Map 并不麻烦,但是我只对一个键值对感兴趣,而且它只在某些情况下出现。

标签: java string


【解决方案1】:

自第 10 版以来,Google Guava 提供了一个类 MapSplitter,它可以做这样的事情:

Map<String, String> params = Splitter
    .on(",")
    .withKeyValueSeparator("=")
    .split("k1=v1,k2=v2");

【讨论】:

    【解决方案2】:

    您可以创建自己的 CSV 解析器,它不是很复杂,但是假设您使用的是标准 CSV 格式,则有一些极端情况需要处理。

    但是为什么要重新发明轮子...

    您可以尝试查找 CSV 解析器,例如

    还有其他的,环顾四周,我相信你会找到适合你需要的。

    【讨论】:

    • 我已经将 OpenCSV 用于这种事情,而且效果很好。
    • 我记得几年前我在寻找这样的库但找不到,所以我制作了自己的解析器。我确实让它工作没问题,但最终花了太多时间来确定所有角落案例。也就是说,我收到的数据并没有真正遵循标准 CSV,所以即使我确实选择了解析器,我也可能不得不稍微调整一下!仍然......如果我能提供帮助,我完全赞成不重新发明轮子
    • 我想对建议滚动另一个 CSV 解析器投反对票,并为推荐一些现有的解析器投赞成票。 Stack Overflow 需要一个左投票按钮。
    • 有些时候现有的库只是不会削减它,很可能是因为其他人决定他们会推出自己的库而不是使用现有的库。尽管如此,我还是改写了答案,强调不要像我那样重新发明,事实上,我同意你的观点:-)
    【解决方案3】:

    通常您会希望将字符串解析为映射,因为您可能会多次提取各种值,因此预先支付解析成本通常是有意义的。

    如果不是,那么我将如何解决这个问题(假设您想区分 int 值和 String 值)。:

    public Object pullValue(String pairs, String key) {
        boolean returnString = false;
        int keyStart = pairs.indexOf(key + "=");
        if (keyStart < 0) {
            logger.error("Key " + key + " not found in key-value pairs string");
            return null;
        }
        int valueStart = keyStart + key.length() + 1;
        if (pairs.charAt(valueStart) == '"') {
            returnString = true;
            valueStart++;    // Skip past the quote mark
        }
        int valueEnd;
        if (returnString) {
            valueEnd = pairs.indexOf('"', valueStart);
            if (valueEnd < 0) {
                logger.error("Unmatched double quote mark extracting value for key " + key)
            }
            return pairs.substring(valueStart, valueEnd);
        } else {
            valueEnd = pairs.indexOf(',', valueStart);
            if (valueEnd < 0) {  // If this is the last key value pair in string
                valueEnd = pairs.length();
            }
            return Integer.decode(pairs.substring(valueStart, valueEnd));
        }
    
    }
    

    请注意,此解决方案假定键、等号和值之间没有空格。如果这些是可能的,您将不得不创建一些代码来在它们之间传递字符串。

    另一种解决方案是使用正则表达式解析器。你可以这样做(这是未经测试的):

    Pattern lookingForString = Pattern.compile(key + "[ \t]*=[ \t]*[\"]([^\"]+)[\"]");
    Pattern lookingForInt = Pattern.compile(key + "[ \t]*=[ \t]*([^,]+)");
    Matcher stringFinder = lookingForString.matcher(pairs);
    Matcher intFinder = lookingForInt.matcher(pairs);
    if (stringFinder.find()) {
        return stringFinder.group(1);
    } else if (intFinder.find()) {
        return Integer.decode(intFinder.group(1));
    } else {
        logger.error("Could not extract value for key " + key);
        return null;
    }
    

    HTH

    【讨论】:

    • 有点苛刻。与其他回复不同,至少我处理了所提出的问题并按照指定的数据处理了数据。确实,我没有推荐通用解析器,因为这不是所要求的。确实存在一些我没有处理的边缘条件(例如处理非整数的未引用值)。但随着问题的出现,我认为这正是我们所要求的,其他所有人都在浪费 OP 的时间。
    • 对于 OP,我应该指出,根据您提供的示例,我对您的数据做出了许多假设:
    • 1) 键值对字符串不会有不必要的空格。 2) 值中只存储了 2 种数据类型,整数和字符串。 3) 所有字符串都用双引号分隔。 4) 所有整数都没有引号,都是整数。 5) 值从不包含等号字符。如果你可以有 'value1="value2=stuff",value2="otherstuff"' 那么其他回复是正确的,你必须解析字符串。如果您保证 '=' 或 '"' 或 ',' 字符永远不会出现在值部分中,则没有理由假定需要解析。
    【解决方案4】:

    用逗号分隔字符串,其他海报是正确的。最好使用 CSV 解析器(您自己的或 OTS)。考虑引号内的逗号等内容可能会导致许多未考虑的问题。

    一旦您在表单中拥有每个单独的令牌:

    key = "value"
    

    我认为查找'=' 的第一个索引很容易。那么之前的部分将是键,之后的部分将是值。然后您可以将它们存储在Map&lt;String, String&gt; 中。 这是假设您的键足够简单,并且其中不包含 = 等。有时,当您可以限制问题范围时,采用简单的路线就足够了。

    【讨论】:

      【解决方案5】:

      如果你只想要这样一个字符串中的一个值,你可以使用 String 的 indexOf() 和 substring() 方法:

      String getValue(String str, String key)
      {
          int keyIndex = str.indexOf(key + "=");
      
          if(keyIndex == -1) return null;
      
          int startIndex = str.indexOf("\"", keyIndex);
          int endIndex = str.indexOf("\"", startIndex);
          String value = str.substring(startIndex + 1, endIndex);
          return value;
      }
      

      【讨论】:

        【解决方案6】:

        首先您应该使用 CSV 解析库来解析逗号分隔值。 正确解析 CSV 数据并不像最初看起来那么简单。有很多 good arguments 不能重新发明那个轮子。

        这也将为您的代码提供未来证明,并且是您不必测试或维护的代码。

        我知道像data.split(','); 这样的诱惑很强烈,但它是脆弱的解决方案。仅举一个例子,如果任何值包含“,”怎么办。

        您应该做的第二件事是解析这些对。同样,使用String.split("="); 的诱惑力会很强烈,但如果= 的右手边有一个=,它可能会很脆弱。

        我不是正则表达式的盲目支持者,但在使用时请谨慎使用,它们可能是完成这项工作的正确工具。这是解析名称值对的正则表达式。

        正则表达式^(.*)\s?=\s?("?([^"]*)"?|"(.*)")$,点击正则表达式在线交互测试。这甚至适用于名称值对右侧的多个双引号。

        这将仅匹配第一个 = 左侧的内容和右侧的所有其他内容,并从字符串值中去除可选的 ",同时仍匹配未引用的数字值。

        给定一个List&lt;String&gt; list 的编码名称值对。

        final Pattern p = Pattern.compile("^(.*)\s?=\s?("?([^"]*)"?|"(.*)")$");
        final Map<String, String> map = new HashMap<String, String>(list.size());
        for (final String nvp : list)
        {
            final Matcher m = p.matcher(nvp);
            m.matches();
            final String name = m.group(1);
            final String value = m.group(2);
            System.out.format("name = %s | value = %s\n", name, value);       
        }
        

        【讨论】:

        • 我同意Spring.split() 是幼稚的,但= 上的匹配器在某种程度上是相同的方法。
        • 您的解决方案是对输入格式的假设,不能被视为有效的通用解决方案...
        • 不应该是一个通用的解决方案,发布了一个他们想要解析的特定输入字符串。我发布了实际使用现代惯用 Java 解析输入字符串的工作代码,你想要的都投反对票。
        • 看,我的解决方案是对输入进行假设。真的。但是,你的也是。你可以带上所有你想要的解析库,它们也需要对输入的结构做出假设。因此,图书馆或拆分,您将需要处理特殊情况。总是。
        • 这些库都为您处理“特殊情况”。这不是真正的特殊情况,它们是 CSV specifications 的一部分,如果您认为它们是“特殊情况”,那么您并不熟悉规范。您是否真的想重新发明、测试和维护鸟巢代码以正确解析 CSV 格式的数据。
        【解决方案7】:

        使用String.split(yourdata, ','),您将获得String[]。然后,对每个条目执行String.split(String[i],"="),以分隔属性和值。

        理想情况下,您应该将此数据移动到Properties 对象实例中。然后,您可以轻松地从 XML 保存/加载它。它有有用的方法。

        REM:我假设您足够了解如果值中包含分隔符(即逗号),则此解决方案将不起作用...

        【讨论】:

        • 使用String.split() 充其量是幼稚的建议,最坏的情况是糟糕的建议。如果数据在值中嵌入了,,会发生什么?如果值中嵌入了" 会发生什么?
        • 使用来自 Google Guava 的 Splitter class 最适合字符串拆分。
        • @Jarrod Roberson 你投票反对我对你对 OP 所做的假设
        • @JVerstry 不,String.split(',') 对提出这个问题的人来说是个糟糕的建议,如果他们问这个问题,他们可能不知道真正解析 CSV 数据需要执行的高级回溯正确。它不是解析 CSV 数据的最佳选择,因为它不会正确解析 CSV 数据,它只处理最狭窄的幼稚情况,有点像建议某人使用正则表达式解析 XML/XHTML,直到它没有;这是大多数情况。
        • @Jarrod Roberson 以你的思路,我可以说你倾向于用炮弹射击苍蝇,因为你对这个问题提出了假设。到目前为止,没有证据表明您提出的案件必须得到处理。你创造了你所看到的世界,而不是它的本来面目。
        猜你喜欢
        • 2010-09-28
        • 1970-01-01
        • 2016-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-04
        • 1970-01-01
        • 2016-10-30
        相关资源
        最近更新 更多