Упростить разбиение строки на альфа-и числовые части

Требование : проанализируйте строку в куски числовых символов и буквенных символов. Альфа-символы должны быть отделены от числа, другие символы следует игнорировать.

Пример данных:

Ввод желаемого результата
1A [1, A]
12 [12]
12G [12, G]
12ABC-SFS513 [12, ABC, SFS, 513]
AGE + W # FE [ВОЗРАСТ, W, FE]
-12WE- [12, WE]
-12- &% 3WE- [12, 3, WE]

Вопрос:

Приведенный ниже код выполняет это. Тем не менее, я ищу любые предложения относительно лучшего способа выполнить это (возможно, сумасшедшее регулярное выражение с использованием String.split()?) Или любые изменения, которые может сделать этот код более удобочитаемым /легко следовать.

Код:

private static String VALID_PATTERN = "[0-9]+|[A-Z]+";

private List<String> parse(String toParse){
    List<String> chunks = new LinkedList<String>();
    toParse = toParse + "$"; //Added invalid character to force the last chunk to be chopped off
    int beginIndex = 0;
    int endIndex = 0;
    while(endIndex < toParse.length()){         
        while(toParse.substring(beginIndex, endIndex + 1).matches(VALID_PATTERN)){
            endIndex++;
        }
        if(beginIndex != endIndex){
            chunks.add(toParse.substring(beginIndex, endIndex));    
        } else {
            endIndex++;
        }  
        beginIndex = endIndex;
    }               
    return chunks;
}
12 голосов | спросил jzd 10 Maypm11 2011, 17:35:46

4 ответа


13

Прежде всего, да, есть сумасшедшее регулярное выражение, которое вы можете дать String.split:

"[^A-Z0-9]+|(?<=[A-Z])(?=[0-9])|(?<=[0-9])(?=[A-Z])"

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

Однако, как вы, вероятно, заметили, приведенное выше регулярное выражение довольно сложнее, чем ваш VALID_PATTERN. Это потому, что вы действительно пытаетесь извлечь определенные части из строки, а не разбить ее.


Таким образом, поиск всех частей строки, соответствующих шаблону и помещающих их в список, является более естественным подходом к проблеме. Это то, что делает ваш код, но он делает это без лишних усилий. Вы можете значительно упростить свой код, просто используя Pattern.matcher следующим образом:

private static final Pattern VALID_PATTERN = Pattern.compile("[0-9]+|[A-Z]+");

private List<String> parse(String toParse) {
    List<String> chunks = new LinkedList<String>();
    Matcher matcher = VALID_PATTERN.matcher(toParse);
    while (matcher.find()) {
        chunks.add( matcher.group() );
    }
    return chunks;
}

Если вы делаете что-то подобное более одного раза, вам может понадобиться реорганизовать тело этого метода в метод findAll, который принимает строки и шаблона в качестве аргументов, а затем называть его findAll(toParse, VALID_PATTERN) в parse.

ответил sepp2k 10 Maypm11 2011, 18:20:59
4

Я уверен, что это возможно с некоторыми безумными RegExp и .split(), но я бы избегал RegExps для «простых» задач, подобных этим.

Одна вещь, которую я бы изменил, - это проверка, соответствует ли «кусок» шаблону. Например, при работе над строкой "AB12", тогда в настоящее время вы сначала проверяете, есть ли "A" соответствует шаблону, а затем, если "AB", но мы уже знаем, что "A", поэтому нет необходимости делать это снова.

Также добавление «недопустимого» символа в конец кажется неправильным способом выхода.

Мое предложение состояло в том, чтобы сделать это так (untested):

private int charType(char c) {
  if ('A' <= c && c <= 'Z')
    return 1;
  else if ('0' <= c && c <= '9')
    return 2;
  else
    return 0;
}

private List<String> parse(String toParse){
    List<String> chunks = new LinkedList<String>();
    int length = toParse.length();
    int beginIndex = 0;
    int endIndex = 0;
    int currentType;
    while (endIndex < length) {
        currentType = charType(toParse.charAt(endIndex));
        if (currentType != 0) {
          do {
              endIndex++;
          } while (endIndex < length && currentType == charType(toParse.charAt(endIndex)));
          chunks.add(toParse.substring(beginIndex, endIndex));    
        } else {
            endIndex++;
        }  
        beginIndex = endIndex;
    }               
    return chunks;
}
ответил RoToRa 10 Maypm11 2011, 18:11:16
2

Если вам все равно, в каком порядке он работает, это сработало для меня

MyString = MyString.replaceAll("[^A-Z ]", "") + " " + MyString.replaceAll("[^0-9 ]", "");
ответил Karcsi Fritz Lehr 10 PM000000110000001531 2012, 23:22:15
1

Вы должны взглянуть на Guava , особенно на CharMatcher и Splitter. Хотя и «ручное» разделение, и регулярные выражения, безусловно, работают, вам не нужно усложнять жизнь, если есть уже легкое и безопасное решение.

ответил Landei 12 Mayam11 2011, 10:57:06

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132