Розбиття рядка на кожен n-й символ


78

У JavaScript це те, як ми можемо розділити рядок на кожен 3-й символ

"foobarspam".match(/.{1,3}/g)

Я намагаюся зрозуміти, як це зробити на Java. Будь-які вказівники?


Я б не використовував Regex для цього завдання.
kennytm

3
гаразд. Що б ви тоді запропонували?
Vijay Dev

1
Щось на зразок відповіді Саймона.
kennytm

Я підтримую вашу рекомендацію. Немає додаткових бібліотек для встановлення, рішення Саймона працювало чудово.
harperville

Відповіді:


134

Ви можете зробити це так:

String s = "1234567890";
System.out.println(java.util.Arrays.toString(s.split("(?<=\\G...)")));

який виробляє:

[123, 456, 789, 0]

Звичайний вираз (?<=\G...)відповідає порожньому рядку, який має останній збіг ( \G), за яким слідують три символи ( ...) перед ним ( (?<= ))


16
Не хотілося б думати, що хтось проголосував за цю відповідь просто тому, що їм не подобаються регулярні вирази.
Вільям Брендель

55
божевільний реквізит для вищого регулярного виразу mojo, але як читач цього коду я б полював на вас і з'їдав ваш будинок. :)
Kevin Bourrillion

4
Якщо ви викликаєте це за допомогою правильно названої функції (тобто splitIntoParts) і не вбудовуєте цей рядок безпосередньо у свій код, це все добре. Інакше нехай полювання починається :)
GreenieMeanie

3
Частина того, що робить цей фокус таким страшним, полягає в тому, що він буде працювати не на всіх мовах. Наприклад, JavaScript не підтримує \G, і Python не розбиватиметься на регулярний вираз, який відповідає нулю символів. Але тоді, якби Java використовувала метод "отримати всі збіги", як це робить будь-яка інша мова, вам би не довелося вигадувати цей фокус спочатку @Bart. ;)
Алан Мур

7
Я копіюю / вставляю це у свій проект Android Studio і отримую [123, 4567890]як результат :(
Еврен Юртесен,

83

Java не пропонує дуже повнофункціональних утиліт для розділення, тому бібліотеки Guava роблять:

Iterable<String> pieces = Splitter.fixedLength(3).split(string);

Перевірте Javadoc для Splitter ; це дуже потужно.


7
+1 Це правильна відповідь (також відома як: знати та користуватися бібліотеками )
Йонік

4
Я б взяв цю відповідь над регулярним виразом ... просто тому, що він є більш ремонтопридатним (наприклад, той факт, що менше людей знає про RegEx, ніж ppl, оскільки вони можуть читати "читабельний" код.)
sivabudh,

4
добре лише в тому випадку, якщо у вас вже є залежність від гуави. В іншому випадку вам потрібно додати ще одну залежність - те, що ви не повинні робити без попередньої перевірки з колегами / архітектором системи.
foo

1
Додавання повної бібліотеки, щоб ви могли просто використовувати один метод, у більшості випадків не є найкращою практикою, а також додавання бібліотеки - це завжди важливе рішення в корпоративному середовищі.
GaboSampaio

50
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        for (String part : getParts("foobarspam", 3)) {
            System.out.println(part);
        }
    }
    private static List<String> getParts(String string, int partitionSize) {
        List<String> parts = new ArrayList<String>();
        int len = string.length();
        for (int i=0; i<len; i+=partitionSize)
        {
            parts.add(string.substring(i, Math.min(len, i + partitionSize)));
        }
        return parts;
    }
}

Якщо ви зберігаєте колекцію підрядків, які охоплюють весь вихідний рядок, новий метод String фактично витратить (n-1) * sizeof (int). Масиви символів нових рядків займуть однакову пам’ять, але кожен з них матиме окреме поле довжини. Тим не менше, якщо пізніше будь-які підрядки буде відкинуто, новий рядок може зменшити пам’ять. Я б не хвилювався в будь-якому випадку, якщо оригінальний рядок не дуже великий.
ILMTitan

@DenisTulskiy чи не могли б Ви пояснити? substringМетод насправді досить розумний , щоб використовувати батьківські струни char[]для даних; див. цю відповідь, щоб дізнатися більше.
wchargin

1
@WChargin: хм, ти маєш рацію, я поняття не маю, чому я написав цей коментар. Я видалю його. Дякую.
Денис Тульський

7

Як доповнення до відповіді Барта Кірса я хочу додати, що можливо замість того, щоб використовувати три крапки ...у виразі регулярного виразу, які представляють три символи, які ви можете написати, .{3}що має однакове значення.

Тоді код буде виглядати наступним чином:

String bitstream = "00101010001001010100101010100101010101001010100001010101010010101";
System.out.println(java.util.Arrays.toString(bitstream.split("(?<=\\G.{3})")));

За допомогою цього було б простіше змінити довжину рядка, і створення функції тепер є розумним із змінною довжиною вхідного рядка. Це можна зробити приблизно так:

public static String[] splitAfterNChars(String input, int splitLen){
    return input.split(String.format("(?<=\\G.{%1$d})", splitLen));
}

Приклад в IdeOne: http://ideone.com/rNlTj5


3

Пізній вхід.

Нижче наводиться стисла реалізація за допомогою потоків Java8, одного вкладиша:

String foobarspam = "foobarspam";
AtomicInteger splitCounter = new AtomicInteger(0);
Collection<String> splittedStrings = foobarspam
                                    .chars()
                                    .mapToObj(_char -> String.valueOf((char)_char))
                                    .collect(Collectors.groupingBy(stringChar -> splitCounter.getAndIncrement() / 3
                                                                ,Collectors.joining()))
                                    .values();

Вихід:

[foo, bar, spa, m]

5
"один лайнер";)
Кріс,

1

Це пізня відповідь, але я все одно викладаю її для всіх нових програмістів:

Якщо ви не хочете використовувати регулярні вирази і не хочете покладатися на сторонні бібліотеки, замість цього ви можете використовувати цей метод, який займає від 89920 до 100113 наносекунд у центральному процесорі 2,80 ГГц (менше мілісекунди). Це не так красиво, як приклад Саймона Нікерсона, але він працює:

   /**
     * Divides the given string into substrings each consisting of the provided
     * length(s).
     * 
     * @param string
     *            the string to split.
     * @param defaultLength
     *            the default length used for any extra substrings. If set to
     *            <code>0</code>, the last substring will start at the sum of
     *            <code>lengths</code> and end at the end of <code>string</code>.
     * @param lengths
     *            the lengths of each substring in order. If any substring is not
     *            provided a length, it will use <code>defaultLength</code>.
     * @return the array of strings computed by splitting this string into the given
     *         substring lengths.
     */
    public static String[] divideString(String string, int defaultLength, int... lengths) {
        java.util.ArrayList<String> parts = new java.util.ArrayList<String>();

        if (lengths.length == 0) {
            parts.add(string.substring(0, defaultLength));
            string = string.substring(defaultLength);
            while (string.length() > 0) {
                if (string.length() < defaultLength) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, defaultLength));
                string = string.substring(defaultLength);
            }
        } else {
            for (int i = 0, temp; i < lengths.length; i++) {
                temp = lengths[i];
                if (string.length() < temp) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, temp));
                string = string.substring(temp);
            }
            while (string.length() > 0) {
                if (string.length() < defaultLength || defaultLength <= 0) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, defaultLength));
                string = string.substring(defaultLength);
            }
        }

        return parts.toArray(new String[parts.size()]);
    }

1

Використання звичайної Java:

    String s = "1234567890";
    List<String> list = new Scanner(s).findAll("...").map(MatchResult::group).collect(Collectors.toList());
    System.out.printf("%s%n", list);

Виводить результат:

[123, 456, 789]

Зверніть увагу, що це відкидає залишки символів (у цьому випадку - 0).


0

Ви також можете розділити рядок на кожен n-й символ і помістити їх кожен у кожен індекс Списку:

Тут я склав список рядків з назвою Послідовність:

Список <Рядок> Послідовність

Тоді я в основному розділяю рядок "KILOSO" на кожні 2 слова. Отже, «KI» «LO» «SO» буде включено в окремий індекс Списку під назвою «Послідовність».

Рядок S = KILOSO

Послідовність = Arrays.asList (S.split ("(? <= \ G ..)"));

Отже, коли я роблю:

System.out.print (послідовність)

На ньому слід надрукувати:

[KI, LO, SO]

для перевірки я можу написати:

System.out.print (Sequence.get (1))

він надрукує:

LO


0

Нещодавно я зіткнувся з цією проблемою, і ось рішення, яке я придумав

final int LENGTH = 10;
String test = "Here is a very long description, it is going to be past 10";

Map<Integer,StringBuilder> stringBuilderMap = new HashMap<>();
for ( int i = 0; i < test.length(); i++ ) {
    int position = i / LENGTH; // i<10 then 0, 10<=i<19 then 1, 20<=i<30 then 2, etc.

    StringBuilder currentSb = stringBuilderMap.computeIfAbsent( position, pos -> new StringBuilder() ); // find sb, or create one if not present
    currentSb.append( test.charAt( i ) ); // add the current char to our sb
}

List<String> comments = stringBuilderMap.entrySet().stream()
        .sorted( Comparator.comparing( Map.Entry::getKey ) )
        .map( entrySet -> entrySet.getValue().toString() )
        .collect( Collectors.toList() );
//done



// here you can see the data
comments.forEach( cmt -> System.out.println( String.format( "'%s' ... length= %d", cmt, cmt.length() ) ) );
// PRINTS:
// 'Here is a ' ... length= 10
// 'very long ' ... length= 10
// 'descriptio' ... length= 10
// 'n, it is g' ... length= 10
// 'oing to be' ... length= 10
// ' past 10' ... length= 8

// make sure they are equal
String joinedString = String.join( "", comments );
System.out.println( "\nOriginal strings are equal " + joinedString.equals( test ) );
// PRINTS: Original strings are equal true
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.