Java正则表达式元字符在拆分时返回额外空间

Java正则表达式元字符在拆分时返回额外空间,java,regex,java-8,metacharacters,Java,Regex,Java 8,Metacharacters,我想使用正则表达式而不是StringTokenizer拆分字符串。我正在使用String.split(regex); 正则表达式包含元字符,当我使用\[时,它在返回数组中返回额外的空间 import java.util.Scanner; public class Solution{ public static void main(String[] args) { Scanner i= new Scanner(System.in); String s= i.

我想使用正则表达式而不是StringTokenizer拆分字符串。我正在使用String.split(regex); 正则表达式包含元字符,当我使用\[时,它在返回数组中返回额外的空间

import java.util.Scanner;
public class Solution{
    public static void main(String[] args) {
        Scanner i= new Scanner(System.in);
        String s= i.nextLine();
        String[] st=s.split("[!\\[,?\\._'@\\+\\]\\s\\\\]+");
        System.out.println(st.length);
        for(String z:st)
            System.out.println(z);
        }
}
当我输入input
[a\m]
它将数组长度返回为3和

 a m  
a前面也有空格。
任何人都可以解释为什么会发生这种情况,以及我如何更正它。我不希望在生成的数组中有额外的空间。

因为
[
位于字符串的开头,当
拆分
删除
[
,在第一个拆分步骤之后会出现两个元素:字符串开头的空字符串和字符串的其余部分。不会仅返回尾随的空元素(默认情况下,它是使用
limit=0
执行的)

从一开始就删除拆分所针对的字符(使用
.replaceAll(“^[!\\[,?.\+\]\\s\\\]+”
,注意模式开头的
^
)。以下是一个示例代码,您可以利用:

String[] st="[a\\m]".replaceAll("^[!\\[,?._'@+\\]\\s\\\\]+", "")
                 .split("[!\\[,?._'@+\\]\\s\\\\]+");
System.out.println(st.length);
for(String z:st) {
    System.out.println(z);
}

请参见

因为
[
位于字符串的开头,所以当
拆分
删除
[
时,在第一个拆分步骤后会出现两个元素:位于字符串开头的空字符串和字符串的其余部分。不会仅返回尾随的空元素(默认情况下,它是以
limit=0
执行的)

从一开始就删除拆分所针对的字符(使用
.replaceAll(“^[!\\[,?.\+\]\\s\\\]+”
,注意模式开头的
^
)。以下是一个示例代码,您可以利用:

String[] st="[a\\m]".replaceAll("^[!\\[,?._'@+\\]\\s\\\\]+", "")
                 .split("[!\\[,?._'@+\\]\\s\\\\]+");
System.out.println(st.length);
for(String z:st) {
    System.out.println(z);
}
请参见

作为补充,您可以通过直接处理包来执行相同的操作,而无需指定两次模式。删除此冗余可以避免潜在错误,并且可能更有效,因为模式不需要解析两次:

Pattern p = Pattern.compile("[!\\[,?\\._'@\\+\\]\\s\\\\]+");
Matcher m = p.matcher(s);
if(m.lookingAt()) s=m.replaceFirst("");
String[] st = p.split(s);
for(String z:st)
    System.out.println(z);
为了能够使用相同的模式,即不必使用锚点
^
来移除前导分隔符,我们首先通过
lookingAt()进行检查
在删除第一个匹配项之前,该模式是否在文本开头真正匹配。然后,我们继续执行
拆分
操作,但重用已经准备好的
模式


关于注释中提到的问题,
split
操作将始终返回至少一个元素,即不匹配的输入字符串,即使该字符串为空。如果希望使用空数组,唯一的解决方案是显式替换结果:

if(st.length==1 && s.equals[0]) st=new String[0];
或者,如果您只想特别处理空字符串,您可以事先检查:

if(s.isEmpty()) st=new String[0];
else {
  // the code as shown above
}
除此之外,您还可以通过直接处理包来执行相同的操作,而无需指定两次模式。删除此冗余可以避免潜在错误,并且可能更有效,因为模式不需要解析两次:

Pattern p = Pattern.compile("[!\\[,?\\._'@\\+\\]\\s\\\\]+");
Matcher m = p.matcher(s);
if(m.lookingAt()) s=m.replaceFirst("");
String[] st = p.split(s);
for(String z:st)
    System.out.println(z);
为了能够使用相同的模式,即不必使用锚点
^
来移除前导分隔符,我们首先通过
lookingAt()进行检查
在删除第一个匹配项之前,该模式是否在文本开头真正匹配。然后,我们继续执行
拆分
操作,但重用已经准备好的
模式


对于注释中提到的问题,即使字符串为空,也会始终返回至少一个元素,即输入字符串,当没有匹配时,即使字符串为空。如果希望有空数组,唯一的解决方案是显式替换结果:

if(st.length==1 && s.equals[0]) st=new String[0];
或者,如果您只想特别处理空字符串,您可以事先检查:

if(s.isEmpty()) st=new String[0];
else {
  // the code as shown above
}

你的意思是要有一个输入字符串
string s=“[a\\m]”
?因为你匹配了
[
]
:你只是得到一个空字符串作为数组中的第一个元素,因为你的输入是从
[
开始的,而拆分是在
上完成的[
也一样。从开头删除所有这些字符。不,它不会删除所有字符,只删除开头的字符。您对这些前导符号不感兴趣,因为它们生成空数组元素,对吗?因此,仅在字符串开头删除它们是一种有效的方法。您的意思是要有一个输入字符串
字符串s=“[a\\m]”
?因为您匹配了
[
]
:您只得到一个空字符串作为数组中的第一个元素,因为您的输入从
[
开始,并且拆分是在
上完成的[
也是。从开头删除所有这些字符。不,它不会删除所有字符,只删除开头的字符。您对这些前导符号不感兴趣,因为它们生成空数组元素,对吗?因此,仅在字符串开头删除它们是一种有效的方法。非常感谢您的详细解释和代码:)先生,如果我只输入!或[它仍然返回1,而它应该返回0:(这是因为当您拆分一个空字符串时,您将得到一个空元素。用于处理这种情况。由于Java 6,您可以使用
string.isEmpty()
而不是
string.length()!=0
,但这只是风格上的不同…我使用“\\\”而不是“”,现在替换时它返回0感谢您的详细解释和代码:)先生,如果我只输入!或者[它仍然返回1,而它应该返回0:(这是因为当拆分空字符串时,您将得到1个空元素。用于处理这种情况。由于Java 6,您可以使用string.isEmpty()
而不是
string.length()!=0
,但这只是风格上的不同…我使用“\\\\\”而不是“”,替换时现在返回0