文字列を分割する方法、および区切り文字を保持する方法は?


243

一連の異なる区切り文字で区切られた複数行の文字列があります。

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

この文字列をを使用してその部分に分割String.splitできますが、区切り文字の正規表現に一致する実際の文字列を取得できないようです。

言い換えれば、これは私が得るものです:

  • Text1
  • Text2
  • Text3
  • Text4

これは私が欲しいものです

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

区切り文字の正規表現を使用して文字列を分割するだけでなく、区切り文字を保持するJDKの方法はありますか?


考えてみて、区切り文字をどこに保持しますか?言葉と一緒に、または別々に?最初のケースでは、それらを前後の単語に付けますか?2番目のケースでは、私の答えはあなたが必要とするものです...
PhiLho 2008年

ちょうどあなたが探しているものを達成するのに役立つはずのクラスを実装しました。下記を参照
VonC '09

回答:


366

先読みと後読みを使用できます。このような:

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の問題です。1つは、これを容易にするために、正規表現が行うことを表す名前の変数を作成し、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
非常に素晴らしい!正規表現の威力を再確認!
ジョージ

1
String#splitを使用してこれを行う方法があるのは嬉しいですが、StringTokenizerのsplit(";", true)場合と同じように区切り文字を含める方法があったらいいのですが、それよりもはるかに読みやすくなりsplit("((?<=;)|(?=;))")ます。
Fabian Steeg、2010

3
つまりString.format(WITH_DELIMITER, ";");、フォーマットは静的メソッドであるためです。
john16384

8
私が今遭遇した複雑さの1つは、[\\s,]+完全に一致させたい可変長区切り文字(たとえば)です。必要な正規表現はさらに長くなります。途中で一致しないようにするために、ネガティブな{ahead、behind}を追加する必要があるためです。(?<=[\\s,]+)(?![\\s,])|(?<![\\s,])(?=[\\s,]+)
のMichałPolitowski

3
2つの区切り文字で分割したい場合はどうなりますか?まあ言ってみれば ';' または「。」
miracle-doh 2016年

78

ルックアラウンドを使用し、ゼロ幅の一致で分割したいとします。ここではいくつかの例を示します。

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 '28年


30

正規表現を含まない非常に素朴な解決策は、区切り文字に次の行に沿って文字列置換を実行することです(区切り文字にコンマを想定):

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

ティルダ(〜)を適切な一意の区切り文字に置き換えることができる場所。

次に、新しい区切り文字で分割を行うと、望ましい結果が得られると思います。


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: ""
*/

反対の方法では、前後に空の要素が表示されるのがあまり好きではありません。区切り文字は通常、文字列の先頭または末尾にないため、ほとんどの場合、2つの適切な配列スロットが無駄になります。

編集:制限ケースを修正しました。テストケースのコメント付きソースは、http//snippets.dzone.com/posts/show/6453にあります。


Wahoo ...ご参加ありがとうございます!興味深いアプローチ。それが一貫して役立つかどうかはわかりませんが(それによって、区切り文字がある場合もあれば、ない場合もあります)、努力に対して+1します。ただし、制限ケース(空またはnull値)に適切に対処する必要があります
VonC '09 / 11/09

このクラスを適切に強化し、徹底的に文書化し、findbugsとcheckstyleを使用してパスを作成し、スニペットWebサイトに公開してください(大量のコードでこのページが
乱雑に

あなたは挑戦に勝ちました!エラー...おめでとうございます!ご存知のように、コードチャレンジスレッドからは、特別なポイントやバッジはありません...(ため息):stackoverflow.com/questions/172184。しかし、この貢献に感謝します。
VonC 2008年

@VonCほとんどの場合、null引数にNPEをスローするのが正しい方法です。静かに処理すると、後でエラーが発生します。
maaartinus

@maaartinus同意しますが、NPEだけでなく、よりユーザーフレンドリーなメッセージをスローしたい場合もあるでしょう。
VonC、

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。


私はそれが間違っているように見えることを知っています-私がちょうどそれから1年後にちょうど今戻ってきたとき、それは私にとって間違っていました。サンプル入力の選択が不十分でした。投稿を編集して、内容を明確にしていきます。
アランムーア


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

@CasimiretHippolyte:賛成票をありがとう。機能しなかったサンプル入力を提供していただけますか。
anubhava 2013年

2
例えば、これは動作しないabcdefとのde区切り文字としてではなく、あなたが使用して問題解決することができます(?!^|$)(?:(?<=de)(?!de)|(?<!de)(?=de))
カシミールらイポリット

1
結果に空の文字列を避けるために、最初のアサーションに留意されたいとき、すなわち区切り文字と文字列の両端(?!^|$)
カシミールらイポリット


9

上記の回答を確認しましたが、正直なところ、どれも満足のいくものではありません。あなたがしたいことは、基本的にPerlの分割機能を模倣することです。なぜJavaがこれを許可せず、どこかにjoin()メソッドを持っているのは私を超えていますが、私は余談です。このためのクラスは本当に必要ありません。その単なる機能。次のサンプルプログラムを実行します。

以前の回答の一部には過剰なnullチェックがあり、最近質問に対する回答をここに書きました:

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

Enumerableなので、StringTokenizerのアイデアが気に入っています。
しかし、これも時代遅れであり、退屈なString []を返すString.splitに置き換えられます(区切り文字は含まれません)。

そのため、Iterableであり、文字列を分割するために真の正規表現を使用するStringTokenizerExを実装しました。

真の正規表現は、区切り文字を形成するために繰り返される「文字シーケンス」ではないことを意味します。
「o」は「o」にのみ一致し、「ooo」は3つの区切り文字に分割され、2つの空の文字列が含まれます。

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

しかし、正規表現o +は「aooob」を分割するときに期待される結果を返します

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

このStringTokenizerExを使用するには:

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 Snippetsで入手できます

以下のためいつものように、コード・チャレンジレスポンス(テストケースと1自己完結型のクラスが含まれている)、それをコピー&ペースト(「SRC /テスト」ディレクトリにある)と、それを実行します。main()メソッドは、さまざまな使用法を示しています。


注:(2009年後半の編集)

記事Final Thoughts:Java Puzzler:Splitting Hairsは、の奇妙な振る舞いを説明する良い仕事をしていString.split()ます。
Josh Blochはその記事に応じてコメントさえしました:

はい、これは苦痛です。FWIW、それは非常に良い理由で行われました:Perlとの互換性。
それをやったのはマイク・マッドボット・マックロスキーで、現在グーグルで働いています。Mikeは、Javaの正規表現が30KのPerl正規表現テストのほぼすべてに合格した(そしてより速く実行された)ことを確認しました。

Google 共通ライブラリGuavaには、次のスプリッターも含まれています。

  • 使いやすい
  • (ユーザーではなく)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.on(',').split(" foo, ,bar, quux,")
--> [" foo", " ", "bar", " quux", ""]

追加の機能が必要な場合は、それらを求めてください!

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

configメソッドの順序は重要ではありません。分割中、空を確認する前にトリミングが行われます。




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はnullチェックを行わない、しない、なぜすべきか。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

私も私の作業バージョンを投稿します(最初は本当にマーカスに似ています)。

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()]);
}

そして、これが2番目のソリューションであり、そのラウンドは最初のソリューションより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の既存の関数(存在しないと言っているわけではありません)は知りませんが、これは私自身の実装です(1つ以上の区切り文字が単一のトークンとして返されます。各区切り文字が個別のトークンとして返されるようにするには、少し調整する必要があります):

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

私はパターンとマッチャーを使用することをお勧めします。正規表現は、String.splitで使用しているものよりも少し複雑にする必要があります。


+1、これは正しい方法です。区切り文字をキャプチャグループに配置すると、StringTokenizerは区切り文字を出力しますが、基本的に非推奨です。split()で先読みを使用することは、受け入れられた回答のコメントで概説されている理由でハッキリです-主に、複数の区切り文字があると混乱することになります。しかし、パターンとマッチャーを使用すると、数行で実際のトークナイザーを使用できます。
johncip 2014年

1

私はそれが可能ではないと思います String#splitますが、を使用StringTokenizerできますが、区切り文字を正規表現として定義することはできず、1桁の文字のクラスとしてのみ定義できます。

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

そこで、区切り文字を指定するための正規表現を定義できません。
Daniel Rikowski、2010

1
ただし、StringTokenizerでは1文字の区切り文字しか使用できません。
マイケルボルグワート

1

余裕がある場合は、Javaのreplace(CharSequenceターゲット、CharSequence置換)メソッドを使用して、分割する別の区切り文字を入力します。例:文字列 "boo:and:foo"を分割し、 ':'を右側の文字列のままにします。

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

重要な注意:これは、文字列に「newdelimiter」がない場合にのみ機能します。したがって、これは一般的な解決策ではありません。しかし、文字列に表示されないことが確実にできるCharSequenceを知っている場合、これは非常に単純なソリューションです。



0

速い答え:\ bのような非物理的な境界を使用して分割します。それが機能するかどうかを試してみます(PHPとJSで使用されています)。

それは可能であり、仕事のようなものですが、あまりに分裂するかもしれません。実際には、分割する文字列と必要な結果によって異なります。詳細をお知らせください。より良いサポートを提供します。

もう1つの方法は、独自の分割を行い、区切り文字をキャプチャして(変数である場合)、後で結果に追加することです。

私の簡単なテスト:

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

以下は、上記のコードの一部に基づいたGroovyバージョンです。とにかく短いです。条件付きで頭と尾を含みます(空でない場合)。最後の部分はデモ/テストケースです。

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

それでも機能する非常に素朴で非効率的なソリューションです.stringでsplitを2回使用してから、2つの配列を連結します

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

この質問の微妙な点の1つに、「先行する区切り文字」の質問があります。トークンと区切り文字を組み合わせた配列を作成する場合は、トークンで始まるか区切り文字であるかを知る必要があります。もちろん、先頭のdelimを破棄する必要があると想定することもできますが、これは不当な想定のようです。また、末尾にdelimがあるかどうかを知りたい場合もあります。これにより、2つのブールフラグが設定されます。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.