Regex для разделения строки с одним или несколькими пробелами и сохранения их [duplicate]

Это работает для fedora 23. Репозитории compat gcc будут немного отличаться в зависимости от вашей версии fedora.

Если вы устанавливаете следующие репозитории:

sudo yum install compat-gcc-34-c++-3.4.6-37.fc23.x86_64 compat-gcc-34-3.4.6-37.fc23.x86_64 

Теперь сделайте как упоминалось выше, предполагается, что ваша папка cuda bin находится в /usr/local/cuda/

sudo ln -s /usr/bin/gcc-34 /usr/local/cuda/bin/gcc
sudo ln -s /usr/bin/g++-34 /usr/local/cuda/bin/g++

Теперь вы можете скомпилировать с nvcc без ошибки версии gcc.

169
задан Daniel Rikowski 5 February 2010 в 11:00
поделиться

20 ответов

Вы можете использовать Lookahead и Lookbehind. Например:

System.out.println(Arrays.toString("a;b;c;d".split("(?<=;)")));
System.out.println(Arrays.toString("a;b;c;d".split("(?=;)")));
System.out.println(Arrays.toString("a;b;c;d".split("((?<=;)|(?=;))")));

И вы получите:

[a;, b;, c;, d]
[a, ;b, ;c, ;d]
[a, ;, b, ;, c, ;, d]

Последнее, что вы хотите.

((?<=;)|(?=;)) равно выбрать пустой символ до ; или после ;.

Надеюсь, что это поможет.

EDIT Замечания Фабиана Стейга о Readability действительны. Считываемость всегда является проблемой для RegEx. Одна вещь, я делаю, чтобы облегчить это - создать переменную, чье имя представляет то, что делает регулярное выражение, и использовать формат строки Java, чтобы помочь этому. Например:

static public final String WITH_DELIMITER = "((?<=%1$s)|(?=%1$s))";
...
public void someMethod() {
...
final String[] aEach = "a;b;c;d".split(String.format(WITH_DELIMITER, ";"));
...
}
...

Это немного помогает. :-D

274
ответ дан luiges90 19 August 2018 в 11:46
поделиться
  • 1
    Очень хорошо! Здесь мы снова видим силу регулярных выражений !! – George 5 February 2010 в 11:26
  • 2
    Хорошее дополнение для улучшения удобочитаемости! – Fabian Steeg 5 February 2010 в 12:46
  • 3
    Это должно быть: String.format(WITH_DELIMITER, ";");, поскольку формат является статическим методом. – john16384 22 April 2012 в 12:18
  • 4
    Одна сложность, с которой я столкнулась, - это ограничители переменной длины (скажем [\\s,]+), которые вы хотите полностью сопоставить. Требуемые регулярные выражения становятся еще длиннее, так как вам нужен дополнительный негативный взгляд (вперед, сзади), чтобы избежать совпадения их в середине, например. (?<=[\\s,]+)(?![\\s,])|(?<![\\s,])(?=[\\s,]+). – Michał Politowski 9 May 2012 в 14:24
  • 5
    что, если я хочу разделить на два разделителя? скажем ';' или '.' – miracle-doh 25 March 2016 в 00:01

Я пришел сюда поздно, но вернувшись к исходному вопросу, почему бы просто не использовать поисковые запросы?

Pattern p = Pattern.compile("(?<=\\w)(?=\\W)|(?<=\\W)(?=\\w)");
System.out.println(Arrays.toString(p.split("'ab','cd','eg'")));
System.out.println(Arrays.toString(p.split("boo:and:foo")));

output:

[', ab, ',', cd, ',', eg, ']
[boo, :, and, :, foo]

EDIT: То, что вы видите выше, что появляется в командной строке при запуске этого кода, но теперь я вижу, что это немного запутанно. Трудно отслеживать, какие запятые являются частью результата и которые были добавлены Arrays.toString(). Подсветка синтаксиса SO также не помогает. В надежде на то, что подсветка будет работать с me вместо меня, вот как эти массивы будут выглядеть, я объявляю их в исходном коде:

{ "'", "ab", "','", "cd", "','", "eg", "'" }
{ "boo", ":", "and", ":", "foo" }

Надеюсь, что это легче читать. Спасибо за хэдз-ап, @finnw.

9
ответ дан Alan Moore 19 August 2018 в 11:46
поделиться
  • 1
    -1 Первый вывод не соответствует вводу – finnw 10 November 2009 в 10:31
  • 2
    Я знаю, что это выглядит неправильно - это выглядело неправильно для меня, когда я вернулся к нему только сейчас, через год после этого факта. Ввод проб был плохо выбран; Я отредактирую сообщение и попытаюсь прояснить ситуацию. – Alan Moore 10 November 2009 в 18:19
  • 3

Я не слишком хорошо знаю Java, но если вы не можете найти метод Split, который делает это, я предлагаю вам просто сделать свой.

string[] mySplit(string s,string delimiter)
{
    string[] result = s.Split(delimiter);
    for(int i=0;i<result.Length-1;i++)
    {
        result[i] += delimiter; //this one would add the delimiter to each items end except the last item, 
                    //you can modify it however you want
    }
}
string[] res = mySplit(myString,myDelimiter);

Не слишком элегантный, но это будет сделано.

-2
ответ дан Alon L 19 August 2018 в 11:46
поделиться

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

String str = "Hello-World:How\nAre You&doing";
inputs = str.split("(?!^)\\b");
for (int i=0; i<inputs.length; i++) {
   System.out.println("a[" + i + "] = \"" + inputs[i] + '"');
}

OUTPUT:

a[0] = "Hello"
a[1] = "-"
a[2] = "World"
a[3] = ":"
a[4] = "How"
a[5] = "
"
a[6] = "Are"
a[7] = " "
a[8] = "You"
a[9] = "&"
a[10] = "doing"

Я использую только границу слова \b, чтобы разграничить слова , кроме , когда это начало текста.

8
ответ дан anubhava 19 August 2018 в 11:46
поделиться
  • 1
    +1 Лучший ответ для меня. но он не работает для буквенно-цифровых разделителей в алфавитно-цифровой строке – Casimir et Hippolyte 13 June 2013 в 13:00
  • 2
    @CasimiretHippolyte: Спасибо за ваше преимущество. Можете ли вы предоставить образец ввода, где он не работает. – anubhava 13 June 2013 в 13:16
  • 3
    например, это не работает для abcdef с de как разделитель, но вы можете решить проблему, используя (?!^|$)(?:(?<=de)(?!de)|(?<!de)(?=de)) – Casimir et Hippolyte 13 June 2013 в 14:45
  • 4
    Обратите внимание на первое утверждение, чтобы избежать пустой строки в результате, когда строка заканчивается разделителем, т. Е. (?!^|$) – Casimir et Hippolyte 13 June 2013 в 15:04
  • 5

Я не знаю о существующей функции в Java API, которая делает это (что не означает, что ее не существует), но вот моя собственная реализация (один или несколько разделителей будут возвращены как один токен; если вы хотите, чтобы каждый разделитель возвращался как отдельный токен, ему потребуется немного адаптации):

static String[] splitWithDelimiters(String s) {
    if (s == null || s.length() == 0) {
        return new String[0];
    }
    LinkedList<String> result = new LinkedList<String>();
    StringBuilder sb = null;
    boolean wasLetterOrDigit = !Character.isLetterOrDigit(s.charAt(0));
    for (char c : s.toCharArray()) {
        if (Character.isLetterOrDigit(c) ^ wasLetterOrDigit) {
            if (sb != null) {
                result.add(sb.toString());
            }
            sb = new StringBuilder();
            wasLetterOrDigit = !wasLetterOrDigit;
        }
        sb.append(c);
    }
    result.add(sb.toString());
    return result.toArray(new String[0]);
}
1
ответ дан bdumitriu 19 August 2018 в 11:46
поделиться

Очень наивное решение, которое не связано с регулярным выражением, состояло в том, чтобы выполнить замену строки на вашем разделителе по строкам (предполагая запятую для разделителя):

string.replace(FullString, "," , "~,~")

Где вы можете заменить тильду (~) с соответствующим уникальным разделителем.

Тогда, если вы разделите свой новый разделитель, я верю, что вы получите желаемый результат.

24
ответ дан chillysapien 19 August 2018 в 11:46
поделиться

Вот простая чистая реализация, которая согласуется с Pattern#split и работает с шаблонами переменной длины, которые выглядят не могут, и это проще в использовании. Это похоже на решение , предоставленное @cletus.

public static String[] split(CharSequence input, String pattern) {
    return split(input, Pattern.compile(pattern));
}

public static String[] split(CharSequence input, Pattern pattern) {
    Matcher matcher = pattern.matcher(input);
    int start = 0;
    List<String> result = new ArrayList<>();
    while (matcher.find()) {
        result.add(input.subSequence(start, matcher.start()).toString());
        result.add(matcher.group());
        start = matcher.end();
    }
    if (start != input.length()) result.add(input.subSequence(start, input.length()).toString());
    return result.toArray(new String[0]);
}

Я не делаю здесь нулевые проверки, Pattern#split не делает, почему я должен. 't нравится if в конце, но требуется для согласованности с Pattern#split. В противном случае я бы безоговорочно добавлял, в результате чего в качестве последнего элемента результата была бы пустая строка, если строка ввода заканчивается шаблоном.

Я конвертирую в String [] для согласованности с Pattern#split, я использую new String[0], а не new String[result.size()], см. здесь здесь .

Вот мои тесты:

@Test
public void splitsVariableLengthPattern() {
    String[] result = Split.split("/foo/$bar/bas", "\\$\\w+");
    Assert.assertArrayEquals(new String[] { "/foo/", "$bar", "/bas" }, result);
}

@Test
public void splitsEndingWithPattern() {
    String[] result = Split.split("/foo/$bar", "\\$\\w+");
    Assert.assertArrayEquals(new String[] { "/foo/", "$bar" }, result);
}

@Test
public void splitsStartingWithPattern() {
    String[] result = Split.split("$foo/bar", "\\$\\w+");
    Assert.assertArrayEquals(new String[] { "", "$foo", "/bar" }, result);
}

@Test
public void splitsNoMatchesPattern() {
    String[] result = Split.split("/foo/bar", "\\$\\w+");
    Assert.assertArrayEquals(new String[] { "/foo/bar" }, result);
}
4
ответ дан Community 19 August 2018 в 11:46
поделиться
  • 1
    Я в замешательстве: у Java есть метод split (), который моделируется на Perl, но гораздо менее мощный. Проблема здесь в том, что split () Java не дает возможности вернуть разделители, чего вы можете достичь в Perl, включив регулярное выражение в скобки. – Alan Moore 11 November 2008 в 01:12
  • 2
    Обратите внимание, что это будет работать только для относительно простых выражений; У меня группа «Look-behind» не имеет очевидной максимальной длины ». пытаясь использовать это с регулярным выражением, представляющим все действительные числа. – daveagp 28 May 2014 в 16:00
  • 3
  • 4
  • 5

Я не думаю, что это возможно с String#split, но вы можете использовать StringTokenizer, хотя это не позволит вам определить ваш разделитель как регулярное выражение, но только как класс однозначных символов :

new StringTokenizer("Hello, world. Hi!", ",.!", true); // true for returnDelims
1
ответ дан Fabian Steeg 19 August 2018 в 11:46
поделиться
  • 1
    Там я не могу определить регулярное выражение, чтобы указать мои разделители. – Daniel Rikowski 5 February 2010 в 11:05
  • 2
    Тем не менее, StringTokenizer допускает только односимвольные разделители. – Michael Borgwardt 5 February 2010 в 11:05
  • 3
    @DR @Michael: правда, отредактировано для уточнения – Fabian Steeg 5 February 2010 в 12:08

Передайте третий аргумент как «true». Он также вернет разделители.

StringTokenizer(String str, String delimiters, true);
3
ответ дан Haseeb Jadoon 19 August 2018 в 11:46
поделиться
  • 1
    Использование StringTokenizer не рекомендуется в новом коде, так как его старый класс ... – plutonium1991 20 May 2015 в 12:16

Другое решение кандидата с использованием регулярного выражения. Сохраняет порядок токенов, правильно соответствует нескольким токенам одного типа в строке. Недостатком является то, что регулярное выражение является довольно неприятным.

package javaapplication2;

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

public class JavaApplication2 {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        String num = "58.5+variable-+98*78/96+a/78.7-3443*12-3";

        // Terrifying regex:
        //  (a)|(b)|(c) match a or b or c
        // where
        //   (a) is one or more digits optionally followed by a decimal point
        //       followed by one or more digits: (\d+(\.\d+)?)
        //   (b) is one of the set + * / - occurring once: ([+*/-])
        //   (c) is a sequence of one or more lowercase latin letter: ([a-z]+)
        Pattern tokenPattern = Pattern.compile("(\\d+(\\.\\d+)?)|([+*/-])|([a-z]+)");
        Matcher tokenMatcher = tokenPattern.matcher(num);

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

        while (!tokenMatcher.hitEnd()) {
            if (tokenMatcher.find()) {
                tokens.add(tokenMatcher.group());
            } else {
                // report error
                break;
            }
        }

        System.out.println(tokens);
    }
}

Образец вывода:

[58.5, +, variable, -, +, 98, *, 78, /, 96, +, a, /, 78.7, -, 3443, *, 12, -, 3]
1
ответ дан Jarvis Cochrane 19 August 2018 в 11:46
поделиться
    String expression = "((A+B)*C-D)*E";
    expression = expression.replaceAll("\\+", "~+~");
    expression = expression.replaceAll("\\*", "~*~");
    expression = expression.replaceAll("-", "~-~");
    expression = expression.replaceAll("/+", "~/~");
    expression = expression.replaceAll("\\(", "~(~"); //also you can use [(] instead of \\(
    expression = expression.replaceAll("\\)", "~)~"); //also you can use [)] instead of \\)
    expression = expression.replaceAll("~~", "~");
    if(expression.startsWith("~")) {
        expression = expression.substring(1);
    }

    String[] expressionArray = expression.split("~");
    System.out.println(Arrays.toString(expressionArray));
0
ответ дан Kanagavelu Sugumar 19 August 2018 в 11:46
поделиться
  • 1
    С regexp это будет: Scanner scanner = new Scanner("((A+B)*C-D)*E"); scanner.useDelimiter("((?<=[\\+\\*\\-\\/\\(\\)])|(?=[\\+\\*\\-\\/\\(\\)]))"); while (scanner.hasNext()) { System.out.print(" " + scanner.next()); } – Tsolak Barseghyan 11 February 2017 в 23:57

Чрезвычайно наивное и неэффективное решение, которое работает, тем не менее. Используйте два раза в строке и затем объединяйте два массива

String temp[]=str.split("\\W");
String temp2[]=str.split("\\w||\\s");
int i=0;
for(String string:temp)
System.out.println(string);
String temp3[]=new String[temp.length-1];
for(String string:temp2)
{
        System.out.println(string);
        if((string.equals("")!=true)&&(string.equals("\\s")!=true))
        {
                temp3[i]=string;
                i++;
        }
//      System.out.println(temp.length);
//      System.out.println(temp2.length);
}
System.out.println(temp3.length);
String[] temp4=new String[temp.length+temp3.length];
int j=0;
for(i=0;i<temp.length;i++)
{
        temp4[j]=temp[i];
        j=j+2;
}
j=1;
for(i=0;i<temp3.length;i++)
{
        temp4[j]=temp3[i];
        j+=2;
}
for(String s:temp4)
System.out.println(s);
0
ответ дан Mariusz Jamro 19 August 2018 в 11:46
поделиться
import java.util.regex.*;
import java.util.LinkedList;

public class Splitter {
    private static final Pattern DEFAULT_PATTERN = Pattern.compile("\\s+");

    private Pattern pattern;
    private boolean keep_delimiters;

    public Splitter(Pattern pattern, boolean keep_delimiters) {
        this.pattern = pattern;
        this.keep_delimiters = keep_delimiters;
    }
    public Splitter(String pattern, boolean keep_delimiters) {
        this(Pattern.compile(pattern==null?"":pattern), keep_delimiters);
    }
    public Splitter(Pattern pattern) { this(pattern, true); }
    public Splitter(String pattern) { this(pattern, true); }
    public Splitter(boolean keep_delimiters) { this(DEFAULT_PATTERN, keep_delimiters); }
    public Splitter() { this(DEFAULT_PATTERN); }

    public String[] split(String text) {
        if (text == null) {
            text = "";
        }

        int last_match = 0;
        LinkedList<String> splitted = new LinkedList<String>();

        Matcher m = this.pattern.matcher(text);

        while (m.find()) {

            splitted.add(text.substring(last_match,m.start()));

            if (this.keep_delimiters) {
                splitted.add(m.group());
            }

            last_match = m.end();
        }

        splitted.add(text.substring(last_match));

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

    public static void main(String[] argv) {
        if (argv.length != 2) {
            System.err.println("Syntax: java Splitter <pattern> <text>");
            return;
        }

        Pattern pattern = null;
        try {
            pattern = Pattern.compile(argv[0]);
        }
        catch (PatternSyntaxException e) {
            System.err.println(e);
            return;
        }

        Splitter splitter = new Splitter(pattern);

        String text = argv[1];
        int counter = 1;
        for (String part : splitter.split(text)) {
            System.out.printf("Part %d: \"%s\"\n", counter++, part);
        }
    }
}

/*
    Example:
    > java Splitter "\W+" "Hello World!"
    Part 1: "Hello"
    Part 2: " "
    Part 3: "World"
    Part 4: "!"
    Part 5: ""
*/

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

Изменить: Исправлены лимиты. Источник комментариев с тестовыми примерами можно найти здесь: http://snippets.dzone.com/posts/show/6453

19
ответ дан Markus Jarderot 19 August 2018 в 11:46
поделиться
  • 1
    Wahoo ... Спасибо за участие! Интересный подход. Я не уверен, что это может быть последовательной (с этим, иногда есть разделитель, иногда нет), но +1 для усилий. Тем не менее, вам все равно необходимо правильно адресовать предельные случаи (пустые или нулевые значения) – VonC 9 November 2008 в 21:11
  • 2
    Я приглашаю вас правильно подкрепить этот класс, тщательно документировать его, сделать проход с помощью findbugs и checkstyle, а затем опубликовать его на веб-сайте (чтобы избежать загромождения этой страницы с помощью большого количества кода) – VonC 9 November 2008 в 21:12
  • 3
    Ты выиграл вызов! Errr ... поздравление! Как вы знаете, из потока кода-кода для этого не было бы особых точек или значков ... (sigh): stackoverflow.com/questions/172184 . Но спасибо за этот вклад. – VonC 10 November 2008 в 09:39
  • 4
    @VonC Большую часть времени, бросая NPE на аргумент null, это правильный путь. Бесшумное обращение с ним приводит к появлению ошибок позже. – maaartinus 15 May 2012 в 16:24
  • 5
    @maaartinus Я согласен, но, конечно, есть пример, где вы хотите бросить более удобное для пользователя сообщение, чем просто NPE, не так ли? – VonC 15 May 2012 в 19:04

Если вы беспокоитесь об осложнениях, которые могут возникнуть в будущем, посмотрите, хотите ли вы использовать метод рок-сплошной утилиты, который может справиться с любым шаблоном токена и любым , которые вы бросаете на него. (Вероятно, это так!) [/ ​​G2]

NB удивлен, обнаружив, что люди Apache Commons, похоже, не предоставили это, например. в StringUtils.

Также я предлагаю, чтобы это было флагом в Pattern: i..e INCLUDE_SEPARATORS.

Но это довольно просто, если вы используете Pattern и Matcher справа:

    // NB could be a different spec for identifying tokens, of course!
    Pattern sepAndTokenPattern = Pattern.compile("(.*?)(\\w+)");
    Matcher matcher = sepAndTokenPattern.matcher( stringForTokenising );
    List<String> tokenAndSeparatorList = new ArrayList<String>();

    // for most processing purposes you are going to want to know whether your 
    // combined list of tokens and separators begins with a token or separator        
    boolean startsWithToken = true;
    int matchEnd = -1;
    while (matcher.find()) {
        String preSep = matcher.group(1);
        if (!preSep.isEmpty()) {
            if( tokenAndSeparatorList.isEmpty() ){
                startsWithToken = false;
            }
            // in implementation you wouldn't want these | characters, of course 
            tokenAndSeparatorList.add("|" + preSep + "|"); // add sep
        }
        tokenAndSeparatorList.add("|" + matcher.group(2) + "|"); // add token
        matchEnd = matcher.end();
    }
    // get trailing separator, if there is one:
    if( matchEnd != -1 ){
        String trailingSep = stringForTokenising.substring( matchEnd );
        if( ! trailingSep.isEmpty() ){
            tokenAndSeparatorList.add( "|" + trailingSep + "|" );
        }
    }

    System.out.println(String.format("# starts with token? %b - matchList %s", startsWithToken, tokenAndSeparatorList));
0
ответ дан mike rodent 19 August 2018 в 11:46
поделиться
  • 1
    30 строк кода действительно действительно просты. – Sebastian Wozny 17 April 2018 в 11:20
  • 2
    @SebastianWozny Ха-ха, ОК, исправлены ... но я сомневаюсь, относится ли мое требование «простоты»? действительно связанный с длиной строк кода ... Я просто использую очень простую (или «симпатичную» простую!) строку шаблонов, прежде всего. Я все еще удивляюсь, что это не должно быть доступно из коробки в Apache Commons или где-то ... Я все время использую Groovy и не удивляюсь, если найду там решение. – mike rodent 17 April 2018 в 11:33
  • 3
    На самом деле есть другой StringTokenizer, org.apache.commons.text.StringTokenizer. Вы можете установить разделитель с помощью stringTokeniser.setDelimiterMatcher( StringMatcherFactory.INSTANCE.xxxMatcher() ) ... там есть полезные помощники, но ни один из них не является RegexMatcher ... хотя, изучая исходный код StringMatcherFactory, вероятно, было бы непросто сделать это. , – mike rodent 17 April 2018 в 11:50
  • 4
    Я инженер-питон. Я в шоке от того, насколько сложно почти что в java. – Sebastian Wozny 17 April 2018 в 15:18
  • 5
    Ах да, я тоже хорошо знаю Python ... Если вам нужно работать на Java, я бы дал вам подсказку: Groovy, который является своего рода «надмножеством». Java, делает довольно хорошую работу в «Pythonising». Ява. Беда в том, что изучить Groovy без изучения Java first сделает вещи еще более сложными. Удачи! PS есть также Jython, который на самом деле является «ароматизатором». из Python, но написанных удивительно на Java, с доступом ко всему (все библиотеки) Java может предложить. Но, в отличие от Groovy, он широко не используется в промышленности. – mike rodent 17 April 2018 в 16:31

Вот отличная версия, основанная на некотором коде выше, на случай, если это поможет. Во всяком случае, это коротко. Условно включает голову и хвост (если они не пусты). Последняя часть представляет собой демонстрационный / тестовый сценарий.

List splitWithTokens(str, pat) {
    def tokens=[]
    def lastMatch=0
    def m = str=~pat
    while (m.find()) {
      if (m.start() > 0) tokens << str[lastMatch..<m.start()]
      tokens << m.group()
      lastMatch=m.end()
    }
    if (lastMatch < str.length()) tokens << str[lastMatch..<str.length()]
    tokens
}

[['<html><head><title>this is the title</title></head>',/<[^>]+>/],
 ['before<html><head><title>this is the title</title></head>after',/<[^>]+>/]
].each { 
   println splitWithTokens(*it)
}
0
ответ дан miles zarathustra 19 August 2018 в 11:46
поделиться

Быстрый ответ: используйте не физические границы, такие как \ b для разделения. Я попробую и поэкспериментирую, чтобы увидеть, работает ли это (используется в PHP и JS).

Возможно, и какая-то работа, но может сильно разделиться. Фактически, это зависит от строки, которую вы хотите разбить, и результата, который вам нужен.

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

Мой быстрый тест:

String str = "'ab','cd','eg'";
String[] stra = str.split("\\b");
for (String s : stra) System.out.print(s + "|");
System.out.println();

Результат:

'|ab|','|cd|','|eg|'|

Слишком много ...: -)

0
ответ дан PhiLho 19 August 2018 в 11:46
поделиться

Изменен Pattern.split () , чтобы включить сопоставленный шаблон в список

Добавлено

// add match to the list
        matchList.add(input.subSequence(start, end).toString());

Полный источник

public static String[] inclusiveSplit(String input, String re, int limit) {
    int index = 0;
    boolean matchLimited = limit > 0;
    ArrayList<String> matchList = new ArrayList<String>();

    Pattern pattern = Pattern.compile(re);
    Matcher m = pattern.matcher(input);

    // Add segments before each match found
    while (m.find()) {
        int end = m.end();
        if (!matchLimited || matchList.size() < limit - 1) {
            int start = m.start();
            String match = input.subSequence(index, start).toString();
            matchList.add(match);
            // add match to the list
            matchList.add(input.subSequence(start, end).toString());
            index = end;
        } else if (matchList.size() == limit - 1) { // last one
            String match = input.subSequence(index, input.length())
                    .toString();
            matchList.add(match);
            index = end;
        }
    }

    // If no match was found, return this
    if (index == 0)
        return new String[] { input.toString() };

    // Add remaining segment
    if (!matchLimited || matchList.size() < limit)
        matchList.add(input.subSequence(index, input.length()).toString());

    // Construct result
    int resultSize = matchList.size();
    if (limit == 0)
        while (resultSize > 0 && matchList.get(resultSize - 1).equals(""))
            resultSize--;
    String[] result = new String[resultSize];
    return matchList.subList(0, resultSize).toArray(result);
}
0
ответ дан Prashant Bhate 19 August 2018 в 11:46
поделиться

Если вы можете себе это позволить, используйте замену Java (CharSequence target, CharSequence replacement) и заполните другой разделитель для разделения. Пример: я хочу разбить строку «boo: and: foo» и сохранить «:» в правой части строки.

String str = "boo:and:foo";
str = str.replace(":","newdelimiter:");
String[] tokens = str.split("newdelimiter");

Важное примечание: это работает только в том случае, если у вас больше нет «newdelimiter» в ваша строка! Таким образом, это не общее решение. Но если вы знаете CharSequence, из которого вы можете быть уверены, что он никогда не появится в String, это очень простое решение.

1
ответ дан Stephan 19 August 2018 в 11:46
поделиться

Я предлагаю использовать Pattern и Matcher, который почти наверняка достигнет того, чего вы хотите. Ваше регулярное выражение должно быть несколько сложнее, чем то, что вы используете в String.split.

1
ответ дан Steve McLeod 19 August 2018 в 11:46
поделиться
  • 1
    +1, Это правильный путь. StringTokenizer будет выводить разделители, если вы поместите их в группы захвата, но это по существу не рекомендуется. Использование lookahead с split () является взломанным по причинам, которые изложены в комментариях принятого ответа - в основном, что это становится беспорядком, когда имеется более одного разделителя. Но вы можете иметь настоящий токенизатор в нескольких строках с помощью Pattern и Matcher. – johncip 14 March 2014 в 11:26

Я также опубликую свои рабочие версии (сначала очень похож на Markus).

public static String[] splitIncludeDelimeter(String regex, String text){
    List<String> list = new LinkedList<>();
    Matcher matcher = Pattern.compile(regex).matcher(text);

    int now, old = 0;
    while(matcher.find()){
        now = matcher.end();
        list.add(text.substring(old, now));
        old = now;
    }

    if(list.size() == 0)
        return new String[]{text};

    //adding rest of a text as last element
    String finalElement = text.substring(old);
    list.add(finalElement);

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

И вот второе решение и его круг на 50% быстрее первого:

public static String[] splitIncludeDelimeter2(String regex, String text){
    List<String> list = new LinkedList<>();
    Matcher matcher = Pattern.compile(regex).matcher(text);

    StringBuffer stringBuffer = new StringBuffer();
    while(matcher.find()){
        matcher.appendReplacement(stringBuffer, matcher.group());
        list.add(stringBuffer.toString());
        stringBuffer.setLength(0); //clear buffer
    }

    matcher.appendTail(stringBuffer); ///dodajemy reszte  ciagu
    list.add(stringBuffer.toString());

    return list.toArray(new String[list.size()]);
}
2
ответ дан Tomasz Mularczyk 19 August 2018 в 11:46
поделиться
Другие вопросы по тегам:

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