这里有一个好的字符串分割方法是什么?

7
我有以下字符串:
A:B:1111;domain:80;a;b
其中A是可选的,所以B:1111;domain:80;a;b也是有效的输入。
:80也是可选的,所以B:1111;domain;a;b:1111;domain;a;b也是有效的输入。
我想得到一个String[],它包含:
s[0] = "A";  
s[1] = "B";  
s[2] = "1111";  
s[3] = "domain:80"  
s[4] = "a"  
s[5] = "b"  

我是这样做的:
List<String> tokens = new ArrayList<String>();  
String[] values = s.split(";");  
String[] actions = values[0].split(":");   

for(String a:actions){  
    tokens.add(a);  
}  
//Start from 1 to skip A:B:1111
for(int i = 1; i < values.length; i++){  
    tokens.add(values[i]);  
}  
String[] finalResult = tokens.toArray();

我在想是否有更好的方法来做这件事?还有其他更高效的方法吗?

1
你尝试过使用:s.split("[;:]")吗?这个正则表达式用于按';'或':'分割字符。 - rascio
域名后面是否总是跟着 80 - codaddict
@codaddict:不,那也是可选的。 - Jim
另一种选择是使用正则表达式,但我非常怀疑你能做出比上面更有效的任何事情。 - Tonny Madsen
5个回答

2
这里没有太多效率问题,我只看到线性。
无论如何,您可以使用正则表达式或手动分词器。
您可以避免使用列表。您知道“values”和“actions”的长度,因此可以执行以下操作:
String[] values = s.split(";");  
String[] actions = values[0].split(":");
String[] result = new String[actions.length + values.length - 1];
System.arraycopy(actions, 0, result, 0, actions.legnth);
System.arraycopy(values, 1, result, actions.length, values.length - 1);
return result;

除非您坚持自己实现 split,否则它应该是相当高效的。

未经测试的低级方法(使用前请务必进行单元测试和基准测试):

// Separator characters, as char, not string.
final static int s1 = ':';
final static int s2 = ';';
// Compute required size:
int components = 1;
for(int p = Math.min(s.indexOf(s1), s.indexOf(s2));
  p < s.length() && p > -1;
  p = s.indexOf(s2, p+1)) {
    components++;
}
String[] result = new String[components];
// Build result
int in=0, i=0, out=Math.min(s.indexOf(s1), s.indexOf(s2));
while(out < s.length() && out > -1) {
  result[i] = s.substring(in, out);
  i++;
  in = out + 1;
  out = s.indexOf(s2, in);
}
assert(i == result.length - 1);
result[i] = s.substring(in, s.length());
return result;

注意:这段代码经过了疯狂的优化,只会在第一个组件中考虑“:”符号。处理最后一个组件有点棘手,因为“out”将具有值“-1”。
通常情况下,我不会使用这种方法,除非性能和内存非常关键。很可能还存在一些错误,并且与上面的代码相比,代码相当难以阅读。

1

通过一些关于可接受字符的假设,这个正则表达式不仅提供了验证功能,还可以将字符串分割成你所需的组。

Pattern p = Pattern.compile("^((.+):)?(.+):(\\d+);(.+):(\\d+);(.+);(.+)$");
Matcher m = p.matcher("A:B:1111;domain:80;a;b");
if(m.matches())
{
    for(int i = 0; i <= m.groupCount(); i++)
        System.out.println(m.group(i));
}
m = p.matcher("B:1111;domain:80;a;b");
if(m.matches())
{
    for(int i = 0; i <= m.groupCount(); i++)
        System.out.println(m.group(i));
}

提供:

A:B:1111;domain:80;a;b // ignore this
A: // ignore this
A // This is the optional A, check for null
B
1111
domain
80
a
b

还有

B:1111;domain:80;a;b // ignore this
null // ignore this
null // This is the optional A, check for null
B
1111
domain
80
a
b

0
除非这是您代码中的瓶颈,并且您已经验证了不要太担心效率,因为这里的逻辑是合理的。 您可以避免创建临时数组列表,而是直接创建数组,因为您知道所需的大小。

我不知道这是否是或将成为瓶颈。但是我有兴趣了解其他提高方法。 - Jim

0

你可以这样做

String str = "A:B:1111;domain:80;a;b";
String[] temp;

/* delimiter */
String delimiter = ";";
/* given string will be split by the argument delimiter provided. */
temp = str.split(delimiter);
/* print substrings */
for(int i =0; i < temp.length ; i++)
System.out.println(temp[i]);

0
如果您想保持域名和端口在一起,那么我相信您需要进行两次分割。您可能可以通过一些正则表达式的魔法来实现,但我怀疑您是否会从中看到任何真正的性能提升。
如果您不介意将域名和端口拆分开来,则:
  String s= "A:B:1111;domain:80;a;b";
  List<String> tokens = new ArrayList<String>();
  String[] values = s.split(";|:");

  for(String a : values){
      tokens.add(a);
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接