Справка на лучшем пути к цифрам синтаксических анализов от Строки в Java

Я люблю второй путь:

something = {'foo' : 'bar',
         'foo2' : 'bar2',
         'foo3' : 'bar3',
         ...
         'fooN': 'barN'}
5
задан Ethan Heilman 4 June 2009 в 19:55
поделиться

8 ответов

Хотели бы вы использовать регулярные выражения, если бы это означало решение проблемы в одной строке кода?

// Split at any position that's either:
// preceded by a digit and followed by a non-digit, or
// preceded by a non-digit and followed by a digit.
String[] parts = str.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)");

С комментарием, объясняющим регулярное выражение, я думаю, что это более читабельно, чем любое другое -regex решения (или любое другое решение для регулярных выражений, если на то пошло).

5
ответ дан 18 December 2019 в 05:40
поделиться

For this particular task I'd always use a regex instead of hand-writing something similar. The code you have given above is, at least to me, less readable than a simple regular expression (which would be (\d+|[^\d]+) in this case, as far as I can see).

You may want to avoid writing regular expressions that exceed a few lines. Those can be and usually are unreadable and hard to understand, but so is the code they can be replaced with! Parsers are almost never pretty and you're usually better off reading the original grammar than trying to make sense of the generated (or handwritten) parser. Same goes (imho) for regexes which are just a concise description of a regular grammar.

So, in general I'd say banning regexes in favor of code like you've given in your question sounds like a terribly stupid idea. And regular expressions are just a tool, nothing less, nothing more. If something else does a better job of text parsing (say, a real parser, some substring magic, etc.) then use it. But don't throw away possibilities just because you feel uncomfortable with them – others may have less problems coping with them and all people are able to learn.

EDIT: Updated regex after comment by mmyers.

13
ответ дан 18 December 2019 в 05:40
поделиться

Для служебный класс, см. java.util.Scanner . Там есть несколько вариантов того, как вы можете решить свою проблему. У меня есть несколько комментариев по вашим вопросам.

Отладчики плохо обрабатывают их (регулярные выражения)

Работает ли регулярное выражение или нет, зависит от того, что в ваших данных. Есть несколько хороших плагинов, которые помогут вам создать регулярное выражение, например QuickREx для Eclipse, действительно ли отладчик помогает вам написать правильный парсер для ваших данных?

Они прерывают поток чтения кем-то исходный код.

Думаю, это зависит от того, насколько вам комфортно с ними. Лично я предпочел бы прочитать разумное регулярное выражение, чем еще 50 строк кода синтаксического анализа строк, но, возможно, это личное дело.

Регулярные выражения со сверхурочной работой разрастаются органически и превращаются в монстров.

Я думаю, что они могут, но это, вероятно, проблема код, в котором они живут, становится расфокусированным. Если сложность исходных данных увеличивается, вам, вероятно, нужно следить за тем, нужно ли вам более выразительное решение (возможно, генератор синтаксического анализатора, такой как ANTLR)

Они глубоко не интуитивно понятны.

Они представляют собой образец соответствующий язык. Я бы сказал, что в этом контексте они довольно интуитивно понятны.

Как я могу улучшить читаемость приведенного выше кода?

Не уверен, кроме использования регулярного выражения.

Есть ли способ лучше? Класс Util, который элегантно решает эту проблему.

Упомянутый выше, java.util.Scanner.

Где вы проводите грань между использованием regEx и кодированием чего-то похожего на то, что я написал выше?

Лично Я использую регулярное выражение для чего-нибудь достаточно простого.

Как повысить удобочитаемость / ремонтопригодность regExes?

Тщательно подумайте перед расширением, особенно тщательно прокомментируйте код и регулярное выражение в деталях, чтобы было понятно, что вы ' повторюсь.

7
ответ дан 18 December 2019 в 05:40
поделиться

Я не слишком Я без ума от регулярных выражений, но это похоже на тот случай, когда они действительно упростят вещи. Что вы, возможно, захотите сделать, так это поместить их в самый маленький метод, который вы можете придумать, назовите его удачно,

1
ответ дан 18 December 2019 в 05:40
поделиться

Я бы использовал что-то вроде этого (предупреждение, непроверенный код). Для меня это намного удобнее, чем пытаться избежать регулярных выражений. Регулярные выражения - отличный инструмент, если их использовать в правильном месте.

Комментирование методов и предоставление примеров входных и выходных значений в комментариях также помогает.

List<String> digitsAsElements(String str){
    Pattern p = Pattern.compile("(\\d+|\\w+)*");
    Matcher m = p.matcher(str);

    List<String> output = new ArrayList<String>();
    for(int i = 1; i <= m.groupCount(); i++) {
       output.add(m.group(i));
    }
    return output;
}
2
ответ дан 18 December 2019 в 05:40
поделиться

Поскольку, похоже, никто еще не опубликовал правильный код, я попробую.

Сначала версия без регулярных выражений. Обратите внимание, что я использую StringBuilder для накопления любого типа символа, который был замечен последним (цифра или не цифра). Если состояние меняется, я выгружаю его содержимое в список и запускаю новый StringBuilder. Таким образом, последовательные нецифровые цифры группируются так же, как и последовательные цифры.

static List<String> digitsAsElements(String str) {
    StringBuilder collector = new StringBuilder();

    List<String> output = new ArrayList<String>();
    boolean lastWasDigit = false;
    for (int i = 0; i < str.length(); i++) {
        char cChar = str.charAt(i);

        boolean isDigit = Character.isDigit(cChar);
        if (isDigit != lastWasDigit) {
            if (collector.length() > 0) {
                output.add(collector.toString());
                collector = new StringBuilder();
            }
            lastWasDigit = isDigit;
        }
        collector.append(cChar);
    }
    if (collector.length() > 0)
        output.add(collector.toString());

    return output;
}

Теперь версия регулярного выражения. По сути, это тот же код, что был опубликован Юхой С., но регулярное выражение действительно работает.

private static final Pattern DIGIT_OR_NONDIGIT_STRING =
        Pattern.compile("(\\d+|[^\\d]+)");
static List<String> digitsAsElementsR(String str) {
    // Match a consecutive series of digits or non-digits
    final Matcher matcher = DIGIT_OR_NONDIGIT_STRING.matcher(str);
    final List<String> output = new ArrayList<String>();
    while (matcher.find()) {
        output.add(matcher.group());
    }
    return output;
}

Один из способов, которым я пытаюсь сохранить читабельность регулярных выражений, - это их имена. Я думаю, что DIGIT_OR_NONDIGIT_STRING довольно хорошо передает то, что я (программист) думаю, что он делает, и тестирование должно убедиться, что он действительно делает то, что должен делать.

public static void main(String[] args) {
    System.out.println(digitsAsElements( "34A312O5MNI444123A"));
    System.out.println(digitsAsElementsR("34A312O5MNI444123A"));
}

печатает:

[34, A, 312, O, 5, MNI, 444123, A]
[34, A, 312, O, 5, MNI, 444123, A]
1
ответ дан 18 December 2019 в 05:40
поделиться

Оууу, кто-то опередил меня в кодировании. Я думаю, что версию с регулярным выражением легче читать / поддерживать. Также обратите внимание на разницу в выводе между двумя реализациями и ожидаемым выводом ...

Вывод:

digitsAsElements1("34A312O5MNI444123A") = [34, A, 312, O, 5, M, , N, , I, 444123, A]
digitsAsElements2("34A312O5MNI444123A") = [34, A, 312, O, 5, MNI, 444123, A]
Expected: [34, A, 312, O, 5, MN, 444123, A]

Сравните:

DigitsAsElements.java:

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DigitsAsElements {

    static List<String> digitsAsElements1(String str){
        StringBuilder digitCollector = new StringBuilder();

        List<String> output = new ArrayList<String>();

        for (int i = 0; i < str.length(); i++){
          char cChar = str.charAt(i);

          if (Character.isDigit(cChar))
             digitCollector.append(cChar);
          else{
            output.add(digitCollector.toString());
            output.add(""+cChar);

            digitCollector = new StringBuilder();
          }         
        }

        return output;
      }

    static List<String> digitsAsElements2(String str){
        // Match a consecutive series of digits or non-digits
        final Pattern pattern = Pattern.compile("(\\d+|\\D+)");
        final Matcher matcher = pattern.matcher(str);

        final List<String> output = new ArrayList<String>();
        while (matcher.find()) {
            output.add(matcher.group());
        }

        return output;
      }

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("digitsAsElements(\"34A312O5MNI444123A\") = " +
                digitsAsElements1("34A312O5MNI444123A"));
        System.out.println("digitsAsElements2(\"34A312O5MNI444123A\") = " +
                digitsAsElements2("34A312O5MNI444123A"));
        System.out.println("Expected: [" +
                "34, A, 312, O, 5, MN, 444123, A"+"]");
    }

}
1
ответ дан 18 December 2019 в 05:40
поделиться

вы можете использовать этот класс, чтобы упростить цикл:

public class StringIterator implements Iterator<Character> {

    private final char[] chars;
    private int i;

    private StringIterator(char[] chars) {
        this.chars = chars;
    }

    public boolean hasNext() {
        return i < chars.length;
    }

    public Character next() {
        return chars[i++];
    }

    public void remove() {
        throw new UnsupportedOperationException("Not supported.");
    }

    public static Iterable<Character> of(String string) {
        final char[] chars = string.toCharArray();

        return new Iterable<Character>() {

            @Override
            public Iterator<Character> iterator() {
                return new StringIterator(chars);
            }
        };
    }
}

Теперь вы можете переписать это:

for (int i = 0; i < str.length(); i++){
    char cChar = str.charAt(i);
    ...
}

с помощью:

for (Character cChar : StringIterator.of(str)) {
    ...
}

мои 2 цента

Кстати, этот класс тоже можно использовать повторно в другом контексте.

1
ответ дан 18 December 2019 в 05:40
поделиться
Другие вопросы по тегам:

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