Как разбить строку с разделителями, если подстрока может быть приведена как int

Я пытаюсь разбить строку на список строк, разделенных на изменение того, может ли символ быть приведен численно или нет. Другими словами, я хочу разбить свою строку на отдельные группы цифр и букв. Для большего удовольствия я также пытаюсь обрезать все начальные 0 из каждой группы чисел. Рассмотрим следующий пример.

Скажем, вы получили "aoeu01234stnh0987" в качестве своего вклада. Результат, который я хочу, это ["aoeu", "1234", "stnh", "987"]

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

private static List<String> fragmentString(String string) {
    char[] charArr = string.toCharArray();
    StringBuilder tempStr = new StringBuilder();
    StringBuilder tempInt = new StringBuilder();
    List<String> tempList = new ArrayList<>();
    boolean wasPrevNum = false;

    for (char c : charArr) {
        boolean isNum = Character.isDigit(c);
        if (isNum) {
            tempInt.append(c);
            if (!wasPrevNum) {
                wasPrevNum = true;
                tempList.add(tempStr.toString());
                tempStr = new StringBuilder();
            }
        } else {
            tempStr.append(c);
            if (wasPrevNum) {
                while (tempInt.charAt(0) == '0') tempInt.deleteCharAt(0);
                tempList.add(tempInt.toString());
                tempInt = new StringBuilder();
                wasPrevNum = false;
            }
        }
    }
    if (tempInt.length() > 0) while (tempInt.charAt(0) == '0') tempInt.deleteCharAt(0);
    tempList.add(wasPrevNum ? tempInt.toString() : tempStr.toString());
    return tempList;
}

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
240
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Очень простым решением может быть использование регулярного выражения. Для поиска подстрок можно использовать регулярное выражение \p{L}+|[0-9]+, которое означает последовательность букв или последовательность цифр. Затем попробуйте разобрать найденную подстроку. Если это целое число, начальные нули будут удалены в результате синтаксического анализа, а если синтаксический анализ не удался, просто напечатайте подстроку.

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

class Main {
    public static void main(String[] args) {
        String str = "aoeu01234stnh0987";
        Matcher matcher = Pattern.compile("\\p{L}+|[0-9]+").matcher(str);
        while (matcher.find()) {
            String substring = matcher.group();
            try {
                System.out.println(Integer.parseInt(substring));
            } catch (NumberFormatException e) {
                System.out.println(substring);
            }
        }
    }
}

Выход:

aoeu
1234
stnh
987

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

tucuxi 12.12.2020 11:49

Этот пример не намного более краток, чем код, опубликованный OP. Лучшее, что я могу сказать, это то, что я не использую исключение как часть своей обработки.

Вот результаты одного тестового прогона.

aoeu01234stnh0987
[aoeu, 1234, stnh, 987]

Вот полный исполняемый пример кода.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class StringSplitter {

    public static void main(String[] args) {
        StringSplitter ss = new StringSplitter();
        
        String input = "aoeu01234stnh0987";
        System.out.println(input);
        List<String> output = ss.splitString(input);
        String[] output2 = output.toArray(new String[output.size()]);
        System.out.println(Arrays.toString(output2));
    }
    
    public List<String> splitString(String input) {
        List<String> output = new ArrayList<>();
        
        if (input == null || input.length() < 1) {
            return output;
        }
        
        char c = input.charAt(0);
        boolean isDigit = Character.isDigit(c);
        StringBuilder builder = new StringBuilder();
        builder.append(c);
        
        for (int i = 1; i < input.length(); i++) {
            c = input.charAt(i);
            
            if (isDigit == Character.isDigit(c)) {
                builder.append(c);
            } else {
                addToList(output, builder, isDigit);
                builder.delete(0, builder.length());
                builder.append(c);
                isDigit = !isDigit;
            }
        }
        
        addToList(output, builder, isDigit);
        return output;
    }

    private void addToList(List<String> output, 
            StringBuilder builder, boolean isDigit) {
        if (isDigit) {
            output.add(Integer.toString(
                    Integer.valueOf(builder.toString())));
        } else {
            output.add(builder.toString());
        }
    }

}

Вы можете добавить несколько символов-разделителей в каждую группу символов, а затем разделить строку вокруг этих символов:

String str = "aoeu01234stnh0987";

String[] arr = str.replaceAll("\\d+|\\D+", "$0::::").split("::::", 0);

System.out.println(Arrays.toString(arr)); // [aoeu, 01234, stnh, 0987]
// trim leading zeros from numbers,
// i.e. parse the integer value
// and return it back to the string
IntStream.range(0, arr.length)
        .filter(i -> arr[i].replaceAll("\\d+", "").length() == 0)
        .forEach(i -> arr[i] = Integer.valueOf(arr[i]).toString());

System.out.println(Arrays.toString(arr)); // [aoeu, 1234, stnh, 987]

See also: How to split the string into string and integer in java?

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

private static List<List<String>> fragmentArr(String[] inputArr) {
    List<List<String>> fragArr = new ArrayList<>();
    Arrays.stream(inputArr).forEach(string -> {
        List<String> listToAdd = new ArrayList<>();
        Matcher matcher = Pattern.compile("[^0-9]+|[0-9]+").matcher(string);
        while (matcher.find()) {
            StringBuilder substring = new StringBuilder(matcher.group());
            while (substring.charAt(0) == '0') substring.deleteCharAt(0);
            listToAdd.add(substring.toString());
        }
        fragArr.add(listToAdd);
    });
    return fragArr;
}

Я использовал цикл while для обрезки 0 вместо преобразования в int и преобразования обратно в строку по двум причинам.

  1. Сложность времени. Если вы конвертируете типы данных для этой задачи, даже используя Big Integer или каким-либо другим способом, вы теряете эффективность. Обратитесь к этому посту о временной сложности преобразования в int и обратно. И parseInt, и toString — операции O(n), где n — вся длина строки. Моя реализация цикла while - O (n), где n - количество ведущих нулей.

  2. Исключение числового формата. Если вам передается строка, например "0000000000000000000001000000000000000000000", будет выдано исключение, если вы попытаетесь преобразовать значение в целое число, чтобы обрезать начальные 0, потому что это значение слишком велико для целочисленного типа данных в Java. Так что это крайний случай для рассмотрения.

Вот модульный тест.

@Test
public void fragmentTest() {
    assertEquals(
            Arrays.asList(
                    Arrays.asList("abc", "123", "dce", "456"),
                    Arrays.asList("123", "abcde", "444", "a")
            ),
            fragmentArr(new String[]{"abc123dce456", "123abcde444a"})

    );
    assertEquals(
            Arrays.asList(
                    Arrays.asList("abc", "1000000000000000000000", "def", "29")
            ),
            fragmentArr(new String[]{"abc0000000000000000000001000000000000000000000def29"})

    );
}

Хорошо, что вы опубликовали свой собственный ответ в дополнение к существующим ответам. Чем больше альтернативных решений, тем полезнее страница становится для будущих посетителей. Однако нехорошо то, что теперь вы не приняли первоначально принятый ответ и принимаете свой собственный ответ. Ни одно сообщество этого не оценит. Тем не менее, вы можете это сделать. В любом случае, желаю вам успехов!

Arvind Kumar Avinash 01.01.2021 18:59

К вашему сведению, я потерял очки репутации, чтобы принять свой собственный ответ, так что это не в мою пользу. Никакие другие ответы оптимально или полностью не решают мою подсказку. Принятый ответ до (вашего) использует попытку для известного ввода и использует преобразование типов для обрезки 0, а регулярное выражение не обрабатывает некоторые альфа-символы, например «-», правильно. Тот, что Гилберт, тоже использует преобразование типов. Lorem, который использует поток для обрезки 0, интересен, но также использует преобразование типов. Во всех ответах не учитываются целые числа, превышающие 4 байта. Я с уважением считаю, что мой ответ лучше всего решает проблему, и я очень ценю ваш ответ.

geekTechnique 01.01.2021 22:46

Ссылаясь на страницу справки , «Выберите один ответ, который, по вашему мнению, является лучшим решением вашей проблемы», я пытаюсь быть объективным.

geekTechnique 01.01.2021 22:48

Другие вопросы по теме