Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何创建与给定字符集匹配的模式?_Java_Regex - Fatal编程技术网

Java 如何创建与给定字符集匹配的模式?

Java 如何创建与给定字符集匹配的模式?,java,regex,Java,Regex,我得到一组字符,例如,作为包含所有字符的字符串,并且需要一个匹配其中任何字符的charclass模式。比如说 对于“abcde”我想要“[a-e]” 对于“[]^-”我想要“[-^\\[\\]]” 如何创建一个紧凑的解决方案,以及如何处理诸如空集和全字符集之类的边界情况 需要逃出什么魔咒 澄清 我想创建一个charclass模式,即类似“[…]”的东西,没有重复,也没有类似的东西。它必须对任何输入都有效,这就是为什么我对角型也感兴趣。匹配所有字符“*”(零次或多次重复匹配任何字符*) 匹配空

我得到一组字符,例如,作为包含所有字符的字符串,并且需要一个匹配其中任何字符的charclass模式。比如说

  • 对于
    “abcde”
    我想要
    “[a-e]”
  • 对于
    “[]^-”
    我想要
    “[-^\\[\\]]”
如何创建一个紧凑的解决方案,以及如何处理诸如空集和全字符集之类的边界情况

需要逃出什么魔咒

澄清 我想创建一个charclass模式,即类似“[…]”的东西,没有重复,也没有类似的东西。它必须对任何输入都有效,这就是为什么我对角型也感兴趣。

匹配所有字符“*”(零次或多次重复匹配任何字符
*

匹配空白行“^ $”(匹配开始的行<代码> ^ < /代码>和结束行>代码> $< /代码>注意缺少在中间行匹配的东西)<


不确定最后一个模式是否正是您想要的,因为“不匹配”有不同的解释。

一个快速、肮脏且几乎不是伪代码的答案:

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]