【问题标题】:Trim a possible prefix of a string in Java修剪Java中字符串的可能前缀
【发布时间】:2014-02-28 00:51:06
【问题描述】:

我有String str,我想从中提取子字符串,不包括可能的前缀"abc"

想到的第一个解决方案是:

if (str.startsWith("abc"))
    return str.substring("abc".length());
return str;

我的问题是:

  1. 使用split"abc" 前缀的正则​​表达式是否有“更清洁”的方法?

  2. 如果是,它是否比上述方法效率低(因为它搜索“整个”字符串)?

  3. 如果是,有没有更好的方法(其中“更好的方法”= 干净高效的解决方案)?

请注意,"abc" 前缀可能出现在字符串的其他位置,不应删除。

谢谢

【问题讨论】:

  • 对“效率”的担忧在这里是相当愚蠢的,除非你试图在每个 X 上做数百万次,这就是瓶颈。您按原样编写代码,可读性好并传达意图

标签: java regex string


【解决方案1】:

比上面的代码短的是这一行:

return str.replaceFirst("^abc", "");

但就性能而言,我想 2 个代码之间不会有任何实质性差异。一个使用正则表达式,一个不使用正则表达式,但会搜索和子字符串。

【讨论】:

  • 如果字符串类似于“123abcxyz”`,它将变为"123xyz"
  • 如果是这种情况,那么这个解决方案将适合我。
  • "abc"不同位置的预期输出是多少?
  • 我稍微编辑了正则表达式,以确保 abc 从一开始就被剥离,否则字符串将保持不变。
  • 如果由于存在锚点^,字符串不以abc开头,它将立即停止
【解决方案2】:

使用String.replaceFirst^abc(匹配前导abc

"abcdef".replaceFirst("^abc", "")     // => "def"
"123456".replaceFirst("^abc", "")     // => "123456"
"123abc456".replaceFirst("^abc", "")  // => "123abc456"

【讨论】:

  • "123abc456".replaceFirst("^abc", "") 呢?
  • replaceFirst 会进行“彻底搜索”,还是会因为'^' 而立即停止?
  • @barakmanos, ^ 仅匹配输入字符串的开头。我没有检查replaceFirst;我不确定它是否会进行彻底的搜索,但我想不会。
【解决方案3】:

一个无正则表达式的解决方案(我需要这个,因为我要删除的字符串是可配置的并且包含反斜杠,需要转义以便在正则表达式中使用文字):

Apache Commons Lang StringUtils.removeStart(str, remove) 将使用String.startsWithString.substringstr 的开头删除remove

该方法的source code提供信息:

public static String removeStart(final String str, final String remove) {
    if (isEmpty(str) || isEmpty(remove)) {
        return str;
    }
    if (str.startsWith(remove)){
        return str.substring(remove.length());
    }
    return str;
}

【讨论】:

  • 我的前缀包含下划线,我真的很讨厌使用双反斜杠来转义。 Java 编译器的第一个反斜杠,Regexp 编译器的第二个反斜杠。
【解决方案4】:

试试这个

str = str.replaceAll("^abc", "");

【讨论】:

  • 如果存在前缀"abc",是否会“切断”前缀?
  • ^ 使其在开头只匹配 abc
【解决方案5】:
  1. 使用String#split 可以做到这一点,但这不是更好的解决方案。实际上它会很模糊,我不建议将其用于此目的。
  2. 在这种情况下不要浪费时间在效率上,这并不重要,重点是逻辑和清晰度。但请注意,使用正则表达式通常会较慢,因为它涉及额外的操作,因此您可能希望保留 startsWith
  3. 您的方法很好,如果您想检查字符串是否以“abc”开头,String#startsWith 就是为此而设计的。

您可以轻松测量代码运行所需的时间。在这里你可以做什么:

创建一个大循环,在其中您可以将其计数器附加到一些虚拟字符串以模拟您要检查的字符串,然后尝试使用一次startsWith,然后尝试使用replaceAll

for(int i = 0;i<900000;i++) {
    StringBuilder sb = new StringBuilder("abc");
    sb.append(i);
    if(sb.toString().startsWith("abc")) { ... } 
}
long time = System.currentTimeMillis() - start;
System.out.println(time); //Prints ~130

for(int i = 0;i<900000;i++){
   StringBuilder sb = new StringBuilder("abc");
   sb.append(i);
   sb.toString().replaceAll("^abc", "");        
}
long time = System.currentTimeMillis() - start;
System.out.println(time);  //Prints ~730

【讨论】:

  • 谢谢,但我没有描述我使用这种方法的整个背景,所以我认为你不能声称我不应该“在这种情况下浪费时间来提高效率” .
  • 这里有几个答案建议str.replaceFirst("^abc", "")。这会同样有效吗?
  • @barakmanos 你打算用多少个字符串?
  • 1,000,000,000,000(需要再添加几个 0 才能添加此评论,但仍然不少)
  • 谢谢ᴍaroun。它应该是replaceFirst 而不是replaceAll。另外,请注意startsWith(...) 的任何使用都必须跟在substring(...)length() 之后,您在上面的测试中没有考虑到这些。我将发布一个关于replaceFirst 性能的单独问题并查看答案(尽管我可能会使用您上面的建议来凭经验断言)。
【解决方案6】:

如果您担心性能,您可以通过使用相同的预编译前缀Pattern 来匹配多个字符串来改进str.replaceFirst("^abc", "") 解决方案。

final Pattern prefix = Pattern.compile("^abc"); // Could be static constant etc
for ... {
    final String result = prefix.matcher(str).replaceFirst("");
}

我想如果你从很多字符串中去掉相同的前缀,差异会很明显。

【讨论】:

    【解决方案7】:

    就效率而言,您可以使用StringBuilder 对一个字符串进行多项操作,例如子字符串、查找索引、子字符串等。


    在清洁度/效率方面,可以使用StringUtils (Apache Commons Lang)

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-11
      • 1970-01-01
      • 2016-08-22
      • 2021-07-22
      • 2014-10-30
      • 2013-09-24
      相关资源
      最近更新 更多