【问题标题】:Get substring between "first two" occurrences of a character获取字符的“前两次”出现之间的子字符串
【发布时间】:2021-12-04 00:57:21
【问题描述】:

我有一个String

 String thestra = "/aaa/bbb/ccc/ddd/eee";

在我的情况下,对于这个 Sting,每次都会出现至少两个斜线。

我得到了/aaa/,如下所示,它是字符串中字符/ 的“前两次出现”之间的子字符串。

 System.out.println("/" + thestra.split("\\/")[1] + "/");

它解决了我的目的,但我想知道是否有任何其他优雅和清洁的替代品?

请注意,aaa 周围需要两个斜线(前导和尾随)。即/aaa/

【问题讨论】:

  • 这个字符串是文件路径吗?
  • 它只是一个字符串。

标签: java string java-8 substring


【解决方案1】:
String thestra = "/aaa/bbb/ccc/ddd/eee";
System.out.println(thestra.substring(0, thestra.indexOf("/", 2) + 1));

【讨论】:

  • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。您可以在帮助中心找到更多关于如何写好答案的信息:stackoverflow.com/help/how-to-answer。祝你好运?
【解决方案2】:

我很惊讶没有人提到从 Java 7 开始使用 Path

String thestra = "/aaa/bbb/ccc/ddd/eee";
String path = Paths.get(thestra).getName(0).toString();
System.out.println("/" + path + "/");
/aaa/

【讨论】:

    【解决方案3】:

    其中一种方法可以用regex[^/]*(/[^/].*?/).* 的组#1 替换字符串,如下所示:

    public class Main {
        public static void main(String[] args) {
            String thestra = "/aaa/bbb/ccc/ddd/eee";
            String result = thestra.replaceAll("[^/]*(/[^/].*?/).*", "$1");
            System.out.println(result);
        }
    }
    

    输出:

    /aaa/
    

    正则表达式的解释:

    • [^/]* :不是角色,/,任意次数
    • ( : 组#1 的开始
      • /:角色,/
      • [^/]:不是角色,/
      • .*?: 任意字符任意次数(惰性匹配)
      • /:角色,/
    • ) : 组#1 结束
    • .* : 任意字符任意次数

    根据Holger的以下宝贵建议更新了答案

    注意,对于Java regex 引擎,/ 没有特殊含义,所以这里不需要转义。此外,由于您只期待一个匹配项(最后的 .* 确保了这一点),所以 replaceFirst 会更惯用。由于没有声明第一个 / 始终位于字符串的开头,因此在模式前面加上 .*?[^/]* 是个好主意。

    【讨论】:

    • 注意,对于Java regex引擎,/没有特殊意义,所以这里不需要转义。此外,由于您只期待一个匹配项(最后的 .* 确保了这一点),所以 replaceFirst 会更惯用。由于没有声明第一个 / 始终位于字符串的开头,因此在模式前面加上 .*?[^/]* 是一个好主意。
    • 感谢@Holger 的宝贵建议。我已将其复制到答案中,因为有时 cmets 会被删除。我逃脱了/ 只是因为我想在 regex101.com 上放一个演示,并在我的代码中使用相同的模式。但是,我忘记在答案中提及相同的内容。您的评论现在完成了?。
    • 嗯,它也适用于 regex101.com。您只需在左侧选择“Java 8”风格,regex101.com/r/zg5N5K/1
    【解决方案4】:

    您还可以匹配前导正斜杠,然后使用negated character class [^/]* 来选择匹配除/ 之外的任何字符,然后匹配尾部正斜杠。

    String thestra = "/aaa/bbb/ccc/ddd/eee";
    Pattern pattern = Pattern.compile("/[^/]*/");
    Matcher matcher = pattern.matcher(thestra);
    
    if (matcher.find()) {
        System.out.println(matcher.group());
    }
    

    输出

    /aaa/
    

    【讨论】:

      【解决方案5】:

      在我的情况下,对于这个 Sting,每次都会出现至少两个斜线

      如果可以保证,请在每个 / 处拆分,保留这些分隔符并获取前三个子字符串。

      String str = String.format("%s%s%s",(thestra.split("((?<=\\/)|(?=\\/))")));
      

      【讨论】:

      • 它给了我Open the Java &gt; Compiler &gt; Errors/Warnings preference page and highlight the problem to configure its severity. Problem: Type String[] of the last argument to method format(String, Object...) doesn't exactly match the vararg parameter type. Cast to Object[] to confirm the non-varargs invocation, or pass individual arguments of type Object for a varargs invocation. 那是什么?
      • @AjayKumar 可变参数和数组的问题。 String.format(string, (Object)…)String.format(string, (Object[])…) 都可以表示,编译器希望确保您知道自己在做什么。将String.format("%s%s%s",… 替换为String.join("",…,不仅警告会消失,而且效率会更高。
      【解决方案6】:

      Scanner::findInLine可以使用返回模式的第一个匹配:

      String thestra = "/aaa/bbb/ccc/ddd/eee";
      System.out.println(new Scanner(thestra).findInLine("/[^/]*/"));
      

      输出:

      /aaa/
      

      【讨论】:

      • 哇。学的太多了。 !!!这看起来超级酷。我以前从未使用过findInLine。肯定会试一试。与我使用的解决方案相比,使用它的性能协商/权衡如何?
      • @AjayKumar 当然,split 为您的任务做了很多不必要的工作。然后,您正在执行字符串连接。所以这种Scanner 方法可能更有效,不过,只有thestra.replaceFirst("[^/]*(/[^/]*/).*", "$1") 可能更有效。无论哪种情况,如果您多次执行此操作,则使用一次Pattern.compile 并重新使用此对象可能更有效。 findInLine 支持指定准备好的 Pattern 而不是 String,对于替换优先的方法,您可以直接在模式上调用 .matcher(thestra).replaceFirst("$1")
      【解决方案7】:

      使用来自java.util.regex 的模式和匹配器。

      Pattern pattern = Pattern.compile("/.*?/");
      Matcher matcher = pattern.matcher(str);
      if (matcher.find()) { 
          String match = matcher.group(0);  // output
      }
      

      【讨论】:

      • 我喜欢它。 * 这里有什么意义?
      • * => 零次或多次。 ? => 贪心,即在第一次搜索时停止 . => 任何字符
      • 咳咳,.*?不情愿,与贪婪相反。 .* 是贪婪的,所以它会匹配到最后一个 /.*? 不情愿所以它匹配到下一个 /
      【解决方案8】:
      Pattern.compile("/.*?/")
                  .matcher(thestra)
                  .results()
                  .map(MatchResult::group)
                  .findFirst().ifPresent(System.out::println);
      

      你可以测试这个变种:)

      此致,Fr0z3Nn

      【讨论】:

      • Stream.limit(1).forEach(System.out::println) 可以用来代替findFirst 返回Optional
      【解决方案9】:

      您可以使用indexOf,它接受第二个参数作为索引开始搜索的起点:

      int start = thestra.indexOf("/");
      int end = thestra.indexOf("/", start + 1) + 1;
      System.out.println(thestra.substring(start, end));
      

      是否更优雅是见仁见智,但至少它不会在字符串中找到每个/ 或创建不必要的数组。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-02
        • 2022-10-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多