字符串中的第N个indexOf?

时间:2018-06-21 17:22:29

标签: java regex string url split

我需要提取URL的子字符串。

URL

  • /service1/api/v1.0/foo-> foo
  • /service1/api/v1.0/foo/{fooId}-> foo/{fooId}
  • /service1/api/v1.0/foo/{fooId}/boo-> foo/{fooId}/boo

其中一些URL可能具有请求参数。

代码

String str = request.getRequestURI();
str = str.substring(str.indexOf("/") + 1);
str = str.substring(str.indexOf("/") + 1);
str = str.substring(str.indexOf("/") + 1);
str = str.substring(str.indexOf("/") + 1, str.indexOf("?"));

是否有更好的方法来提取子字符串,而不是反复使用indexOf方法?

4 个答案:

答案 0 :(得分:1)

有很多替代方法:

  1. 在带有\分隔符的分割字符串上使用Java-Stream API:

    String str = "/service1/api/v1.0/foo/{fooId}/boo";  
    String[] split = str.split("\\/");
    String url = Arrays.stream(split).skip(4).collect(Collectors.joining("/"));
    System.out.println(url);
    

    消除参数后,Stream将像:

    String url = Arrays.stream(split)
        .skip(4)
        .map(i -> i.replaceAll("\\?.+", ""))
        .collect(Collectors.joining("/"));
    
  2. 这也是Regex的位置!使用类Pattern和Matcher。

    String str = "/service1/api/v1.0/foo/{fooId}/boo";
    
    Pattern pattern = Pattern.compile("\\/.*?\\/api\\/v\\d+\\.\\d+\\/(.+)");
    Matcher matcher = pattern.matcher(str);
    while (matcher.find()) {
        System.out.println(matcher.group(1));
    }
    

如果您依赖indexOf(..)的用法,则可能要使用while循环。

String str = "/service1/api/v1.0/foo/{fooId}/boo?parameter=value"; 

String string = str;
while(!string.startsWith("v1.0")) {
    string = string.substring(string.indexOf("/") + 1);
}

System.out.println(string.substring(string.indexOf("/") + 1, string.indexOf("?")));

其他答案包括一种方法,如果前缀不可更改,则您可能只想使用一次idndexOf(..)方法(@JB Nizet)的调用:

string.substring("/service1/api/v1.0/".length(), string.indexOf("?"));

所有这些解决方案都基于您的输入和事实,模式是已知的,或者至少是上一节的数字以\或版本v1.0为检查点-最佳解决方案由于网址的数量不受限制,因此此处可能不会出现。您必须知道输入URL的所有可能组合,才能找到最佳的处理方式。

答案 1 :(得分:0)

如何?

String s = "/service1/api/v1.0/foo/{fooId}/boo";
String[] sArray = s.split("/"); 
StringBuilder sb = new StringBuilder();

for (int i = 4; i < sArray.length; i++) {
    sb.append(sArray[i]).append("/");
}

sb.deleteCharAt(sb.length() - 1);
System.out.println(sb.toString());

输出:

foo/{fooId}/boo

如果url前缀始终为/service1/api/v1.0/,则只需执行s.substring("/service1/api/v1.0/".length())

答案 2 :(得分:0)

Path为此非常有用:

public static void main(String[] args) {
    Path root = Paths.get("/service1/api/v1.0/foo");
    Path relativize = root.relativize(Paths.get("/service1/api/v1.0/foo/{fooId}/boo"));
    System.out.println(relativize);
}

输出:

  

{fooId} / boo

答案 3 :(得分:0)

这里有一些不错的选择。

1)如果您知道“ foo”将始终是第四个标记,那么您已经有了正确的想法。唯一的问题是您拥有提高效率所需的信息,但您并未使用它。不必多次复制String并从新String的开头重新开始,您只需从上次中断的地方继续4次,即可找到所需的起点。

String str = "/service1/api/v1.0/foo/{fooId}/boo";

// start at the beginning
int start = 0;
// get the 4th index of '/' in the string
for (int i = 0; i != 4; i++) {
    // get the next index of '/' after the index 'start'
    start = str.indexOf('/',start);
    // increase the pointer to the next character after this slash
    start++;
}
// get the substring
str = str.substring(start);

这将比任何正则表达式模式都高效得多。

2)正则表达式:(java.util.regex。*)。如果您想要的东西始终放在"service1/api/v1.0/"之前,这将起作用。之前可能还有其他目录,例如"one/two/three/service1/api/v1.0/"

// \Q \E will automatically escape any special chars in the path
// (.+) will capture the matched text at that position
// $ marks the end of the string (technically it matches just before '\n')
Pattern pattern = Pattern.compile("/service1/api/v1\\.0/(.+)$");
// get a matcher for it
Matcher matcher = pattern.matcher(str);
// if there is a match
if (matcher.find()) {
    // get the captured text
    str = matcher.group(1);
}

如果您的路径可能有所不同,则可以使用正则表达式来解决。例如:也可以通过将正则表达式更改为"/service\\d*/api/v\\d+(?:\\.\\d+)?/(.+)(?:/|$)"

来匹配service / api / v3 / foo / {bar} / baz /“(注意数字格式和尾随'/')