Java 如何创建与给定字符集匹配的模式?
我得到一组字符,例如,作为包含所有字符的字符串,并且需要一个匹配其中任何字符的charclass模式。比如说Java 如何创建与给定字符集匹配的模式?,java,regex,Java,Regex,我得到一组字符,例如,作为包含所有字符的字符串,并且需要一个匹配其中任何字符的charclass模式。比如说 对于“abcde”我想要“[a-e]” 对于“[]^-”我想要“[-^\\[\\]]” 如何创建一个紧凑的解决方案,以及如何处理诸如空集和全字符集之类的边界情况 需要逃出什么魔咒 澄清 我想创建一个charclass模式,即类似“[…]”的东西,没有重复,也没有类似的东西。它必须对任何输入都有效,这就是为什么我对角型也感兴趣。匹配所有字符“*”(零次或多次重复匹配任何字符*) 匹配空
- 对于
我想要“abcde”
“[a-e]”
- 对于
我想要“[]^-”
“[-^\\[\\]]”
*
)
匹配空白行“^ $”(匹配开始的行<代码> ^ < /代码>和结束行>代码> $< /代码>注意缺少在中间行匹配的东西)<
不确定最后一个模式是否正是您想要的,因为“不匹配”有不同的解释。一个快速、肮脏且几乎不是伪代码的答案:
StringBuilder sb = new StringBuilder("[");
Set<Character> metaChars = //...appropriate initialization
while (sourceString.length() != 0) {
char c = sourceString.charAt(0);
sb.append(metaChars.contains(c) ? "\\"+c : c);
sourceString.replace(c,'');
}
sb.append("]");
Pattern p = Pattern.compile(sb.toString());
//...can check here for the appropriate sb.length cases
// e.g, 2 = empty, all chars equals the count of whatever set qualifies as all chars, etc
空集是
[^\u0000-\uFFFF]
,所有字符的集合是[\u0000-\uFFFF]
。不确定需要前者做什么,因为它与任何内容都不匹配。我会在空字符串上抛出IllegalArgumentException()
需要逃出什么魔咒
-
^
[
]
-所有这些,我实际上已经对其进行了测试。与其他一些正则表达式实现不同,[
被认为是字符类中的元字符,可能是因为可能使用带运算符的内部字符类
任务的其余部分听起来很简单,但相当乏味。首先,您需要选择唯一的字符。然后循环它们,附加到StringBuilder,可能是转义。如果您想要字符范围,则需要首先对字符进行排序,并在循环时选择连续的范围。如果您希望-
位于范围的开头如果没有转义,则设置一个标志,但不要附加它。循环后,如果设置了标志,则在将其包装到[]
中之前,先将-
添加到结果中:
import java.util.*;
public class RegexUtils {
private static String encode(char c) {
switch (c) {
case '[':
case ']':
case '\\':
case '-':
case '^':
return "\\" + c;
default:
return String.valueOf(c);
}
}
public static String createCharClass(char[] chars) {
if (chars.length == 0) {
return "[^\\u0000-\\uFFFF]";
}
StringBuilder builder = new StringBuilder();
boolean includeCaret = false;
boolean includeMinus = false;
List<Character> set = new ArrayList<Character>(new TreeSet<Character>(toCharList(chars)));
if (set.size() == 1<<16) {
return "[\\w\\W]";
}
for (int i = 0; i < set.size(); i++) {
int rangeLength = discoverRange(i, set);
if (rangeLength > 2) {
builder.append(encode(set.get(i))).append('-').append(encode(set.get(i + rangeLength)));
i += rangeLength;
} else {
switch (set.get(i)) {
case '[':
case ']':
case '\\':
builder.append('\\').append(set.get(i));
break;
case '-':
includeMinus = true;
break;
case '^':
includeCaret = true;
break;
default:
builder.append(set.get(i));
break;
}
}
}
builder.append(includeCaret ? "^" : "");
builder.insert(0, includeMinus ? "-" : "");
return "[" + builder + "]";
}
private static List<Character> toCharList(char[] chars) {
List<Character> list = new ArrayList<Character>();
for (char c : chars) {
list.add(c);
}
return list;
}
private static int discoverRange(int index, List<Character> chars) {
int range = 0;
for (int i = index + 1; i < chars.size(); i++) {
if (chars.get(i) - chars.get(i - 1) != 1) break;
range++;
}
return range;
}
public static void main(String[] args) {
System.out.println(createCharClass("daecb".toCharArray()));
System.out.println(createCharClass("[]^-".toCharArray()));
System.out.println(createCharClass("".toCharArray()));
System.out.println(createCharClass("d1a3e5c55543b2000".toCharArray()));
System.out.println(createCharClass("!-./0".toCharArray()));
}
}
印刷品:
[a-e]
[-\[\]^]
[^\u0000-\uFFFF]
[0-5a-e]
[!\--0]
字符类中的角案例包括:
\
[
]
它需要一个
\
来转义。字符^
如果不放在字符类的开头,就不需要转义,而-
如果放在字符类的开头或结尾,就不需要转义(因此我的代码中有布尔
标志).那么你想为regex编写一个字符串解析器吗?我没有太多考虑,但感觉这将非常困难、耗时且容易出错。在这种情况下,我会问自己:为什么我需要这个?@Jan我认为这是一个理论上的例子,即使没有实际的用例。我并不声称我需要它。但是,在字符集的不同表示形式之间进行转换的方法通常是有用的。它必须是一个charclass,因此我不能使用您的模式。现在,我认为以下方法可行:“[a&&[^a]]”
。如果您希望字符类与任何字符匹配,只需使用旧的JavaScript备用:“[\\s]”
不确定您需要前者做什么,因为它与任何内容都不匹配。我会在空字符串上抛出一个IllegalArgumentException()。我知道它与任何内容都不匹配。但是抛出或其他任何内容都是完全错误的,它必须按规定执行,而不执行其他任何操作。做得好。它在“^”中失败,我想我最好简单地避开^
和-
而不是处理特殊情况。测试set.size()==0xFFFF
必须是set.size() == 1@maaartinus,很好的捕获!你可以在开始时添加-
,在课程结束时添加^
。还有一个问题:!-./0
导致[!--0]
,这很可能意味着“!”到“-”和“0”,这是错误的。总是转义似乎是一种方法。还有一种方法:对于奇怪的String s=new String(Character.toChars(0x12345));
包含使用代理项对表示的单个字符,表达式Pattern.compile([^\\u0000-\\uFFFF]”).matcher(s).matches();
有效。@maaartinus,是的,也是这样!)添加了一个小的encode(char)
方法来正确编码范围的开始和结束。但是正如您所提到的:只需转义-
和^
“它不会将事物转换为范围(我认为这很好-这样做对我来说有过早优化的味道)。”-这样,当我使用补码集时,我会很快以64k字符长的正则表达式结束。
"daecb".toCharArray()
"[]^-".toCharArray()
"".toCharArray()
"d1a3e5c55543b2000".toCharArray()
[a-e]
[-\[\]^]
[^\u0000-\uFFFF]
[0-5a-e]
[!\--0]