Как создать шаблон, соответствующий заданному набору символов? - PullRequest
2 голосов
/ 28 февраля 2011

Я получаю набор символов, например, в виде строки, содержащей все из них, и мне нужен шаблон класса, соответствующий любому из них.Например,

  • для "abcde" Я хочу "[a-e]"
  • для "[]^-" Я хочу "[-^\\[\\]]"

Как создать компактРешение и как обрабатывать случаи границы, такие как пустой набор и набор всех символов?

Какие символы необходимо экранировать?

Уточнение

Я хочу создать шаблон charclass,то есть что-то вроде «[...]», без повторов и тому подобного.Он должен работать для любого входа, поэтому меня интересуют и угловые случаи.

Ответы [ 4 ]

1 голос
/ 28 февраля 2011

Вот начало:

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()));
    }
}

Как видите, ввод:

"daecb".toCharArray()
"[]^-".toCharArray()
"".toCharArray()
"d1a3e5c55543b2000".toCharArray()

печатает:

[a-e]
[-\[\]^]
[^\u0000-\uFFFF]
[0-5a-e]
[!\--0]

Угловые регистры в символекласс:

  • \
  • [
  • ]

, для которого потребуется \ длябыть сбежавшимПерсонажу ^ не нужно убегать, если он не расположен в начале класса персонажа, а - не нужно экранировать, когда он находится в начале или в концекласс символов (отсюда boolean флаги в моем коде).

1 голос
/ 28 февраля 2011

Пустой набор - [^\u0000-\uFFFF], а набор всех символов - [\u0000-\uFFFF].Не уверен, для чего вам нужен первый, потому что он не будет ничего соответствовать.Я бы вместо этого бросил IllegalArgumentException () в пустую строку.

Какие символы необходимо экранировать?

- ^ \ [ ] - это все, я на самом деле это проверял.И в отличие от некоторых других реализаций регулярных выражений [ считается метасимволом внутри класса символов, возможно из-за возможности использования внутренних классов символов с операторами.

Остальная часть задачи звучит легко, но довольно утомительно.Сначала нужно выбрать уникальных персонажей.Затем переберите их, добавляя к StringBuilder, возможно, экранируя.Если вам нужны диапазоны символов, вам нужно сначала отсортировать символы и выбрать непрерывные диапазоны во время цикла.Если вы хотите, чтобы - находился в начале диапазона без экранирования, установите флаг, но не добавляйте его.После цикла, если флаг установлен, добавьте - к результату перед тем, как обернуть его в [].

0 голосов
/ 28 февраля 2011

Быстрый, грязный и почти не псевдокодовый ответ:

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

, который дает вам уникальную строку символов, с которой вы хотите сопоставить, с метасимволами заменены.Это не будет преобразовывать вещи в диапазоны (что я думаю хорошо - для меня это пахнет преждевременной оптимизацией).Вы можете выполнить некоторые пост-тесты для простых установленных случаев - например, сопоставление sb с цифрами, нецифрами и т. Д., Но если вы не знаете, что это принесет вам большую производительность (или упрощениесмысл этой программы), я бы не стал беспокоиться.

Если вы действительно хотите сделать диапазоны, вы можете вместо этого sourceString.toCharArray() отсортировать, повторить удаление повторений и выполнить какую-то проверку диапазона и заменить метасимволы.когда вы добавляете содержимое в StringBuilder.

РЕДАКТИРОВАТЬ: Мне на самом деле понравилась версия toCharArray, так что псевдокодирован также:

//...check for empty here, if not...
char[] sourceC = sourceString.toCharArray();
Arrays.sort(sourceC);
lastC = sourceC[0];
StringBuilder sb = new StringBuilder("[");
StringBuilder range = new StringBuilder();
for (int i=1; i<sourceC.length; i++) {
  if (lastC == sourceC[i]) continue;
  if (//.. next char in sequence..//) //..add to range
  else {
    // check range size, append accordingly to sb as a single item, range, etc
  }
  lastC = sourceC[i];
}
0 голосов
/ 28 февраля 2011

Совпадение всех символов ". *" (Ноль или более повторений * совпадения с любым символом ..

Соответствует пустой строке «^ $» (соответствует началу строки ^ и концу строки $. Обратите внимание, что в середине строки отсутствует совпадение).

Не уверен, что последний шаблон именно то, что вы хотели, так как есть разные интерпретации, чтобы "ничего не соответствовать".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...