如何拆分字符串,但还要保留定界符?


243

我有一个多行字符串,该字符串由一组不同的定界符定界:

(Text1)(DelimiterA)(Text2)(DelimiterC)(Text3)(DelimiterB)(Text4)

我可以使用将该字符串拆分为多个部分,String.split但似乎无法获取与定界符regex匹配的实际字符串。

换句话说,这就是我得到的:

  • Text1
  • Text2
  • Text3
  • Text4

这就是我要的

  • Text1
  • DelimiterA
  • Text2
  • DelimiterC
  • Text3
  • DelimiterB
  • Text4

是否有任何JDK使用分隔符regex拆分字符串但还保留分隔符的方法?


想一想,您想在哪里保留分隔符?还是用单词还是分开?在第一种情况下,您会将它们附加到前面还是后面的单词上?在第二种情况下,我的答案就是您所需要的...
PhiLho

刚刚实现了一个类,该类应该可以帮助您实现所需的目标。见下文
VonC

Answers:


366

您可以使用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]

最后一个是您想要的。

((?<=;)|(?=;))等于选择之前;或之后的空字符;

希望这可以帮助。

编辑 Fabian Steeg对可读性的评论有效。可读性始终是RegEx的问题。我要做的一件事是缓解这种情况,即创建一个变量,该变量的名称表示正则表达式的作用,并使用Java String格式来帮助实现这一点。像这样:

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


2
非常好!在这里,我们可以再次看到正则表达式的强大功能!
乔治,2010年

1
很高兴看到有一个使用String#split进行此操作的方法,尽管我希望有一种方法可以包含定界符,就像StringTokenizer一样- split(";", true)可读性比split("((?<=;)|(?=;))")
Fabian Steeg,2010年

3
应该是:String.format(WITH_DELIMITER, ";");因为format是静态方法。
john16384

8
我刚遇到的一个难题是[\\s,]+您要完全匹配的可变长度定界符(例如)。所需的正则表达式会变得更长,因为您需要额外的负向外观{ahead,behind},以避免在中间进行匹配。(?<=[\\s,]+)(?![\\s,])|(?<![\\s,])(?=[\\s,]+)
米哈尔Politowski

3
如果我想用两个定界符分开怎么办?我们说“;” 要么 '。'
miracle-doh

77

您要使用环视,并在零宽度匹配项上进行拆分。这里有些例子:

public class SplitNDump {
    static void dump(String[] arr) {
        for (String s : arr) {
            System.out.format("[%s]", s);
        }
        System.out.println();
    }
    public static void main(String[] args) {
        dump("1,234,567,890".split(","));
        // "[1][234][567][890]"
        dump("1,234,567,890".split("(?=,)"));   
        // "[1][,234][,567][,890]"
        dump("1,234,567,890".split("(?<=,)"));  
        // "[1,][234,][567,][890]"
        dump("1,234,567,890".split("(?<=,)|(?=,)"));
        // "[1][,][234][,][567][,][890]"

        dump(":a:bb::c:".split("(?=:)|(?<=:)"));
        // "[][:][a][:][bb][:][:][c][:]"
        dump(":a:bb::c:".split("(?=(?!^):)|(?<=:)"));
        // "[:][a][:][bb][:][:][c][:]"
        dump(":::a::::b  b::c:".split("(?=(?!^):)(?<!:)|(?!:)(?<=:)"));
        // "[:::][a][::::][b  b][::][c][:]"
        dump("a,bb:::c  d..e".split("(?!^)\\b"));
        // "[a][,][bb][:::][c][  ][d][..][e]"

        dump("ArrayIndexOutOfBoundsException".split("(?<=[a-z])(?=[A-Z])"));
        // "[Array][Index][Out][Of][Bounds][Exception]"
        dump("1234567890".split("(?<=\\G.{4})"));   
        // "[1234][5678][90]"

        // Split at the end of each run of letter
        dump("Boooyaaaah! Yippieeee!!".split("(?<=(?=(.)\\1(?!\\1))..)"));
        // "[Booo][yaaaa][h! Yipp][ieeee][!!]"
    }
}

是的,那是最后一个模式中三重嵌套的断言。

相关问题

也可以看看


1
注意,这仅适用于相对简单的表达式。我遇到了一个“隐藏组没有明显的最大长度”,试图将其与代表所有实数的正则表达式一起使用。
daveagp 2014年


30

一个不涉及正则表达式的非常幼稚的解决方案是在分隔符上执行字符串替换(假设分隔符使用逗号):

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

您可以在其中用适当的唯一定界符替换tilda(〜)。

然后,如果您对新的定界符进行了拆分,那么我相信您会获得理想的结果。


24
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


Wahoo ...感谢您的参与!有趣的方法。我不确定是否能始终如一地为您提供帮助(因此有时会有定界符,有时没有定界符),但是需要+1。但是,您仍然需要正确处理极限情况(空值或空值)
VonC

我邀请您适当地加强此类,彻底地记录下来,使用findbugs和checkstyle进行传递,然后将其发布在摘录网站上(以避免用大量的代码混淆此页面)
VonC

您赢得了挑战!嗯...恭喜!如您所知,在代码挑战线程中,没有特殊的要点或标志...(叹气):stackoverflow.com/questions/172184。但是,谢谢您的贡献。
VonC

@VonC大多数时候,在null参数上抛出NPE 是正确的方法。静默处理它会导致以后出现错误。
maaartinus 2012年

@maaartinus我同意,但是可以肯定,在某些情况下,您想发出比NPE更用户友好的消息,对吗?
VonC 2012年

11

我来晚了,但是回到原来的问题,为什么不仅仅使用环顾四周呢?

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")));

输出:

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

编辑:您在上面看到的是运行该代码时出现在命令行上的内容,但是现在我看到它有点令人困惑。很难跟踪哪些逗号是结果的一部分,哪些是由添加的Arrays.toString()。SO的语法突出显示也无济于事。希望让突出显示我一起使用而不是与我一起使用,这是我在源代码中声明的那些数组的外观:

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

我希望这更容易阅读。感谢您的单挑,@ finnw。


我知道这看起来很不对头-事实发生一年后,当我刚回到它时,对我来说,这看起来就不对劲。样本输入选择不当;我将编辑帖子并尝试澄清问题。
艾伦·摩尔


10

我知道这是一个非常古老的问题,答案也已被接受。但我仍然想对原始问题提出一个非常简单的答案。考虑以下代码:

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] + '"');
}

输出:

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来分隔单词,除非它是文本的开头。


1
+1对我来说最好的答案。但不适用于字母数字字符串中的字母数字分隔符
Casimir et Hippolyte 2013年

@CasimiretHippolyte:感谢您的支持。您能否在无法使用的地方提供示例输入。
anubhava 2013年

2
例如这不适合工作abcdefde为分隔符,但你可以使用解决问题(?!^|$)(?:(?<=de)(?!de)|(?<!de)(?=de))
卡西米尔等伊波利特

1
请注意第一个断言,当字符串以定界符结尾时,避免在结果中留空字符串,即(?!^|$)
Casimir et Hippolyte 2013年


9

我看了上面的答案,说实话我都不满意。您要做的实际上是模仿Perl split功能。为什么Java不允许这样做,为什么在某个地方有一个join()方法超出了我的范围,但我离题了。您甚至不需要为此真正上课。它只是一个功能。运行此示例程序:

一些较早的答案有过多的空检查,我最近在这里对一个问题进行了回复:

https://stackoverflow.com/users/18393/cletus

无论如何,代码:

public class Split {
    public static List<String> split(String s, String pattern) {
        assert s != null;
        assert pattern != null;
        return split(s, Pattern.compile(pattern));
    }

    public static List<String> split(String s, Pattern pattern) {
        assert s != null;
        assert pattern != null;
        Matcher m = pattern.matcher(s);
        List<String> ret = new ArrayList<String>();
        int start = 0;
        while (m.find()) {
            ret.add(s.substring(start, m.start()));
            ret.add(m.group());
            start = m.end();
        }
        ret.add(start >= s.length() ? "" : s.substring(start));
        return ret;
    }

    private static void testSplit(String s, String pattern) {
        System.out.printf("Splitting '%s' with pattern '%s'%n", s, pattern);
        List<String> tokens = split(s, pattern);
        System.out.printf("Found %d matches%n", tokens.size());
        int i = 0;
        for (String token : tokens) {
            System.out.printf("  %d/%d: '%s'%n", ++i, tokens.size(), token);
        }
        System.out.println();
    }

    public static void main(String args[]) {
        testSplit("abcdefghij", "z"); // "abcdefghij"
        testSplit("abcdefghij", "f"); // "abcde", "f", "ghi"
        testSplit("abcdefghij", "j"); // "abcdefghi", "j", ""
        testSplit("abcdefghij", "a"); // "", "a", "bcdefghij"
        testSplit("abcdefghij", "[bdfh]"); // "a", "b", "c", "d", "e", "f", "g", "h", "ij"
    }
}

我很困惑:Java确实有一个split()方法,该方法以Perl的模型为模型,但功能却差很多。这里的问题是Java的split()没有提供返回定界符的方法,您可以在Perl中通过将正则表达式包含在捕获括号中来实现。
艾伦·摩尔


7

我喜欢StringTokenizer的想法,因为它是可枚举的。
但是它也已过时,并替换为String.split,它返回无聊的String [](并且不包括定界符)。

因此,我实现了一个StringIterable,它是一个Iterable,并使用一个真正的正则表达式来拆分字符串。

真正的正则表达式表示它不是重复的“字符序列”以形成定界符:
“ o”仅匹配“ o”,并将“ ooo”分为三个定界符,其中包含两个空字符串:

[o], '', [o], '', [o]

但是,正则表达式o +在拆分“ aooob”时将返回预期结果

[], 'a', [ooo], 'b', []

要使用此StringTokenizerExEx:

final StringTokenizerEx aStringTokenizerEx = new StringTokenizerEx("boo:and:foo", "o+");
final String firstDelimiter = aStringTokenizerEx.getDelimiter();
for(String aString: aStringTokenizerEx )
{
    // uses the split String detected and memorized in 'aString'
    final nextDelimiter = aStringTokenizerEx.getDelimiter();
}

此类的代码可在DZone摘录中获得

与通常的代码挑战响应(一个包含测试用例的自包含类)一样,将其复制粘贴(在“ src / test”目录中)并运行它。它的main()方法说明了不同的用法。


注意:(2009年后期编辑)

文章最终思想:Java难题者:分裂头发很好地解释了中的怪异行为String.split()
乔什·布洛赫(Josh Bloch)甚至对这篇文章发表了评论:

是的,这很痛苦。FWIW,这样做的理由非常充分:与Perl兼容。
做到这一点的人是Mike“ madbot” McCloskey,他现在在Google与我们合作。Mike确保Java的正则表达式实际上通过了30K Perl正则表达式测试中的每一个(并且运行得更快)。

Google 通用图书馆Guava还包含一个Splitter,它是:

  • 使用更简单
  • 由Google(而不是您)维护

因此可能值得检查。从最初的原始文档(pdf)

JDK具有以下功能:

String[] pieces = "foo.bar".split("\\.");

如果需要确切的功能,可以使用它:-正则表达式-结果作为数组-处理空块的方式

迷你益智游戏:“,a ,, b,”。split(“,”)返回...

(a) "", "a", "", "b", ""
(b) null, "a", null, "b", null
(c) "a", null, "b"
(d) "a", "b"
(e) None of the above

答案:(e)以上都不是。

",a,,b,".split(",")
returns
"", "a", "", "b"

仅跳过尾随的容器!(谁知道解决方法可以防止跳过?这很有趣...)

无论如何,我们的Splitter都更加灵活:默认行为很简单:

Splitter.on(',').split(" foo, ,bar, quux,")
--> [" foo", " ", "bar", " quux", ""]

如果您需要其他功能,请提出要求!

Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.split(" foo, ,bar, quux,")
--> ["foo", "bar", "quux"]

配置方法的顺序无关紧要-在拆分期间,修剪会在检查空之前进行。




4

这是一个简单的干净实现,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不是,为什么呢。我不喜欢if结尾处的,但是为了与保持一致是必需的Pattern#split。否则,我将无条件追加,如果输入字符串以模式结尾,则将空字符串作为结果的最后一个元素。

为了与保持一致,我转换为String [] Pattern#splitnew 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);
}

2

我还将发布我的工作版本(第一个确实与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

另一种使用正则表达式的候选解决方案。保留令牌顺序,连续正确匹配多个相同类型的令牌。缺点是正则表达式有点讨厌。

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

我不知道执行此操作的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

我建议使用Pattern和Matcher,几乎可以肯定会实现您想要的。您的正则表达式将需要比String.split中使用的正则表达式复杂一些。


+1,这是正确的方法。如果将您放置在捕获组中,StringTokenizer将输出定界符,但实际上已弃用。由于接受的答案的注释中概述了一些原因,因此将split()与lookahead一起使用很不明智-主要是当有多个定界符时,它变得一团糟。但是您可以在Pattern和Matcher的几行中找到一个真正的标记器。
johncip

1

我认为无法使用String#split,但是您可以使用StringTokenizer,尽管这不允许您将分隔符定义为正则表达式,而只能定义为一位数的字符:

new StringTokenizer("Hello, world. Hi!", ",.!", true); // true for returnDelims

在那里,我无法定义用于指定分隔符的正则表达式。
Daniel Rikowski

1
不过,StringTokenizer仅允许使用单字符定界符。
Michael Borgwardt'2

1

如果可以的话,请使用Java的replace(CharSequence目标,CharSequence替换)方法并填写另一个分隔符以进行拆分。示例:我想分割字符串“ boo:and:foo”,并在其右边的字符串处保留“:”。

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

重要说明:仅当字符串中没有其他“ newdelimiter”时,此方法才有效!因此,这不是一般的解决方案。但是,如果您知道一个CharSequence,可以确保它永远不会出现在String中,那么这是一个非常简单的解决方案。



0

快速答案:使用\ 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

调整了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

如果有帮助,这是基于上面一些代码的普通版本。总之很短。有条件地包括头和尾(如果它们不为空)。最后一部分是演示/测试案例。

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

仍然非常幼稚和低效的解决方案,对字符串使用split两次,然后将两个数组连接起来

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
    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));

使用正则表达式将是:Scanner scanner = new Scanner("((A+B)*C-D)*E"); scanner.useDelimiter("((?<=[\\+\\*\\-\\/\\(\\)])|(?=[\\+\\*\\-\\/\\(\\)]))"); while (scanner.hasNext()) { System.out.print(" " + scanner.next()); }
Tsolak Barseghyan

0

这个问题中的一个微妙之处涉及“前导定界符”问题:如果您要具有标记和定界符的组合数组,则必须知道它是以标记还是定界符开头的。您当然可以假设应该丢弃前导的delim,但这似乎是不合理的假设。您可能还想知道是否有尾随的delim。这将相应地设置两个布尔标志。

用Groovy编写,但是Java版本应该相当明显:

            String tokenRegex = /[\p{L}\p{N}]+/ // a String in Groovy, Unicode alphanumeric
            def finder = phraseForTokenising =~ tokenRegex
            // NB in Groovy the variable 'finder' is then of class java.util.regex.Matcher
            def finderIt = finder.iterator() // extra method added to Matcher by Groovy magic
            int start = 0
            boolean leadingDelim, trailingDelim
            def combinedTokensAndDelims = [] // create an array in Groovy

            while( finderIt.hasNext() )
            {
                def token = finderIt.next()
                int finderStart = finder.start()
                String delim = phraseForTokenising[ start  .. finderStart - 1 ]
                // Groovy: above gets slice of String/array
                if( start == 0 ) leadingDelim = finderStart != 0
                if( start > 0 || leadingDelim ) combinedTokensAndDelims << delim
                combinedTokensAndDelims << token // add element to end of array
                start = finder.end()
            }
            // start == 0 indicates no tokens found
            if( start > 0 ) {
                // finish by seeing whether there is a trailing delim
                trailingDelim = start < phraseForTokenising.length()
                if( trailingDelim ) combinedTokensAndDelims << phraseForTokenising[ start .. -1 ]

                println( "leading delim? $leadingDelim, trailing delim? $trailingDelim, combined array:\n $combinedTokensAndDelims" )

            }

-2

我不太了解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);

它不太优雅,但是可以。


但是如果连续有多个定界符怎么办?
Kip

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.