Javaで文字列を同じ長さの部分文字列に分割する


125

文字列"Thequickbrownfoxjumps"をJavaで同じサイズの部分文字列に分割する方法。例えば。"Thequickbrownfoxjumps"サイズが4の場合、出力が得られます。

["Theq","uick","brow","nfox","jump","s"]

同様の質問:

Scalaで文字列を同じ長さの部分文字列に分割する


4
何を試しましたか?なぜうまくいかなかったのですか?
Thilo、2009

2
これには正規表現を使用する必要がありますか?正規表現タグのせいで尋ねる...
Tim Pietzcker

彼が投稿した@ThiloリンクはScala向けで、Javaでも同じように質問しています
Jaydeep Patel

@Thilo:私は、スカラに対して与えられた答えのように、Javaでそれをどのように行うかを尋ねていました。
エミル

回答:


226

これは正規表現のワンライナーバージョンです。

System.out.println(Arrays.toString(
    "Thequickbrownfoxjumps".split("(?<=\\G.{4})")
));

\G前の一致が終了した位置と一致するゼロ幅のアサーションです。以前に一致するものなかった場合、と同様に、入力の先頭と一致し\Aます。囲み後読みは、最後の一致の終わりから4文字後の位置に一致します。

後読みと\G高度な正規表現機能はどちらも、すべてのフレーバーでサポートされているわけではありません。さらに、\Gそれをサポートするフレーバー全体で一貫して実装されていません。このトリックは(たとえば)Java、Perl、.NETおよびJGSoftでは機能しますが、PHP(PCRE)、Ruby 1.9+またはTextMate(どちらもOniguruma)では機能しません。JavaScript /y(スティッキーフラグ)はほど柔軟ではなく\G、JSが後読みをサポートしていたとしても、この方法では使用できませんでした。

他のオプションがある場合は、このソリューションを必ずしもお勧めするわけではありません。他の回答の非正規表現ソリューションは長くなる可能性がありますが、自己文書化されています。これはちょうどそのです。;)

また、これはAndroidでは機能せず、\G後読みでの使用をサポートしていません。


2
PHP 5.2.4では次のコードが機能します。return preg_split( '/(?<= \ G。{'。$ len。 '})/ u'、$ str、-1、PREG_SPLIT_NO_EMPTY);
イゴール

5
記録のためString.substring()に、正規表現の代わりに使用すると、数行のコードを追加する必要がありますが、どこかで5倍高速に実行されます...
ムーアを描いた

2
Javaでは、これは改行を含む文字列に対しては機能しません。最初の改行までチェックするだけで、その改行が分割サイズの前にある場合、文字列は分割されません。または私は何かを逃したのですか?
joensson 2014

5
完全を期すために、テキストを複数行に分割するに(?s)は、正規表現で接頭辞を付ける必要があります:(?s)(?<=\\G.{4})
bobbel

1
Javaは完全にコンパイル時にこの上barfs:java.util.regex.PatternSyntaxException: Look-behind pattern matches must have a bounded maximum length
ジェフリーBlattman

132

まあ、単純な算術演算と文字列演算でこれを行うのはかなり簡単です:

public static List<String> splitEqually(String text, int size) {
    // Give the list the right capacity to start with. You could use an array
    // instead if you wanted.
    List<String> ret = new ArrayList<String>((text.length() + size - 1) / size);

    for (int start = 0; start < text.length(); start += size) {
        ret.add(text.substring(start, Math.min(text.length(), start + size)));
    }
    return ret;
}

これに正規表現を使用する価値は本当にないと思います。

編集:正規表現を使用しない私の理由:

  • これは、正規表現の実際のパターンマッチングを使用しません。数えているだけです。
  • 私は疑うほとんどの場合、それは問題ではないであろうが、上記をより効率的になります
  • さまざまな場所で可変サイズを使用する必要がある場合は、繰り返しまたはヘルパー関数を使用して、パラメーターに基づいて正規表現自体を構築します。
  • 別の回答で提供された正規表現は、最初はコンパイルされず(無効なエスケープ)、次に機能しませんでした。私のコードは初めて動作しました。これは、正規表現対IMOの使いやすさの証です。

8
@エミル:実際、あなた正規表現を要求しませんでした。タグ内にありますが、質問自体には正規表現を要求するものはありません。このメソッドを1か所に配置すると、コード内の任意の場所で文字列を1つの非常に読みやすいステートメント分割できます。
Jon Skeet、2009

3
Emilこれは正規表現の対象ではありません。限目。
Chris

3
@Emil:文字列を分割するためのワンライナーが必要な場合Splitter.fixedLength(4)は、シーナイザーが提案するGuavaをお勧めします。
ColinD 2010

2
@Jay:来てくださいあなたはそれほど皮肉である必要はありません.1行で正規表現を使用してそれを行うことができると確信しています。固定長の部分文字列もパターンです。この答えについてどう思いますか?stackoverflow.com/questions/3760152/…
Emil

4
@エミル:私はそれを失礼にするつもりはありませんでした。私のポイントの深刻な部分は、そうですが、これを行うには正規表現を考え出すことができると確信しています-アラン・ムーアが彼が機能すると主張しているものを持っていると思います-それは謎めいたものであり、それゆえ後のプログラマーにとって困難です理解し、維持します。部分文字列ソリューションは直感的で読みやすいものにすることができます。ジョン・スキートの第4弾を参照してください。100%同意します。
ジェイ・

71

これはGoogle Guavaで非常に簡単です

for(final String token :
    Splitter
        .fixedLength(4)
        .split("Thequickbrownfoxjumps")){
    System.out.println(token);
}

出力:

Theq
uick
brow
nfox
jump
s

または、配列として結果が必要な場合は、次のコードを使用できます。

String[] tokens =
    Iterables.toArray(
        Splitter
            .fixedLength(4)
            .split("Thequickbrownfoxjumps"),
        String.class
    );

参照:

注:スプリッターの構成は上記のインラインで示されていますが、スプリッターは不変で再利用可能なため、定数に格納することをお勧めします。

private static final Splitter FOUR_LETTERS = Splitter.fixedLength(4);

// more code

for(final String token : FOUR_LETTERS.split("Thequickbrownfoxjumps")){
    System.out.println(token);
}

私は正規表現回答受け入れる必要があるでしょう.But(グアバライブラリー法の私を認識させるために)ポストをありがとうstackoverflow.com/questions/3760152/...を、それが任意のサードパーティのライブラリとワンライナーを必要としないからです。
Emil

1
この単純なタスクを実行するためだけに数百KBのライブラリコードを含めることは、ほぼ間違いなく正しいことではありません。
Jeffrey Blattman、2016年

2
このためだけにGuavaを含む@JeffreyBlattmanは、おそらくやりすぎです。しかし、私はなぜこの機能の一つの追加枚使用しないで、とにかくすべての私のJavaコードの汎用ライブラリとしてそれを使用する
ショーン・パトリック・フロイド

セパレーターで結合する方法はありますか?
アクエリアスパワー

1
@AquariusPowerString.join(separator, arrayOrCollection)
Holger

14

Googleのguava汎用ライブラリーを使用している場合(そして、正直なところ、新しいJavaプロジェクトはおそらくそうあるはずです)、これはSplitterクラスではめったに取るに足らないことです。

for (String substring : Splitter.fixedLength(4).split(inputString)) {
    doSomethingWith(substring);
}

それはだ、それ。簡単!


8
public static String[] split(String src, int len) {
    String[] result = new String[(int)Math.ceil((double)src.length()/(double)len)];
    for (int i=0; i<result.length; i++)
        result[i] = src.substring(i*len, Math.min(src.length(), (i+1)*len));
    return result;
}

以来src.length()len両方あるintの、あなたの呼び出しはceiling (src.length()+ LEN - 1)/ LEN:他の応答のいくつかはそれをやっているかチェックしてください-あなたが望むものを達成されていない
マイケル・ビール・デイヴィス

@マイケル:良い点。非長さの文字列ではテストしていません。現在は修正されています。
ソール

6
public String[] splitInParts(String s, int partLength)
{
    int len = s.length();

    // Number of parts
    int nparts = (len + partLength - 1) / partLength;
    String parts[] = new String[nparts];

    // Break into parts
    int offset= 0;
    int i = 0;
    while (i < nparts)
    {
        parts[i] = s.substring(offset, Math.min(offset + partLength, len));
        offset += partLength;
        i++;
    }

    return parts;
}

6
興味津々で、forループに対して何かありますか?
Jon Skeet、2009

forループは確かに、このアウトを指しているため、この:-)おかげで、より「自然」の選択肢を使用することです。
グロドリゲス

3

substringfrom String.class(例外の処理)またはApache lang commons(例外の処理)を使用できます

static String   substring(String str, int start, int end) 

ループの中に入れればいい。


1
substring標準Stringクラスのメソッドの何が問題になっていますか?
グロドリゲス

コモンズバージョンは例外(範囲外など)を回避
Thilo

7
そうですか; 代わりに、呼び出しコードのパラメーターを制御することで、「例外を回避」したいと思います。
Grodriguez

2

私はむしろこの単純な解決策を望みます:

String content = "Thequickbrownfoxjumps";
while(content.length() > 4) {
    System.out.println(content.substring(0, 4));
    content = content.substring(4);
}
System.out.println(content);

これを行わないでください!文字列は不変なので、コードでは残りの文字列全体を4文字ごとにコピーする必要があります。したがって、スニペットは、文字列のサイズが線形時間ではなく2次時間になります。
トビアス

@Tobias:Stringが可変であったとしても、このスニペットは前述の冗長なコピーを実行しますが、複雑なコンパイルプロセスが存在する場合を除きます。このスニペットを使用する唯一の理由は、コードが単純であることです。
Cheetah Coder 2016

最初に投稿してからコードを変更しましたか?最新バージョンは実際にはコピーを作成しません-substring()は効率的に実行されます(少なくともJavaの古いバージョンでは一定の時間)。(少なくとも古いバージョンのJavaでは)文字列全体のchar []への参照を保持しますが、すべての文字を保持しているため、この場合は問題ありません。したがって、ここにある最新のコードは実際には問題ありません(内容が空の文字列で始まる場合、コードが空の行を出力することを想定して、意図したものとは異なる場合があります)。
トビアス

@トビアス:私は何の変化も覚えていません。
Cheetah Coder

@Tobias substringのJava 7 2012の中央に更新6で変更実装offsetし、countフィールドから除去されたStringクラス。したがって、の複雑さは、substringこの回答がなされるずっと前に線形になりました。しかし、例のような小さな文字列の場合、それでも十分高速に実行され、長い文字列の場合でも、このタスクが実際に発生することはほとんどありません。
Holger

2

Java8ストリームを使用したワンライナー実装は次のとおりです。

String input = "Thequickbrownfoxjumps";
final AtomicInteger atomicInteger = new AtomicInteger(0);
Collection<String> result = input.chars()
                                    .mapToObj(c -> String.valueOf((char)c) )
                                    .collect(Collectors.groupingBy(c -> atomicInteger.getAndIncrement() / 4
                                                                ,Collectors.joining()))
                                    .values();

次の出力が表示されます。

[Theq, uick, brow, nfox, jump, s]

1
これは恐ろしい解決策であり、ボクシングと文字列連結のオーバーヘッドについては言わないで、APIの意図と戦い、ステートフル関数を使用し、通常のループよりもはるかに複雑です。Streamソリューションが必要な場合は、次のようなものを使用しますString[] result = IntStream.range(0, (input.length()+3)/4) .mapToObj(i -> input.substring(i *= 4, Math.min(i + 4, input.length()))) .toArray(String[]::new);
Holger

2

以下は、Java 8 IntStreamを使用してスライス開始のインデックスを決定するワンライナーバージョンです。

String x = "Thequickbrownfoxjumps";

String[] result = IntStream
                    .iterate(0, i -> i + 4)
                    .limit((int) Math.ceil(x.length() / 4.0))
                    .mapToObj(i ->
                        x.substring(i, Math.min(i + 4, x.length())
                    )
                    .toArray(String[]::new);

1

文字列を均等に後方に分割する場合、つまり右から左に、たとえばに分割1010001111する[10, 1000, 1111]場合は、次のコードを使用します。

/**
 * @param s         the string to be split
 * @param subLen    length of the equal-length substrings.
 * @param backwards true if the splitting is from right to left, false otherwise
 * @return an array of equal-length substrings
 * @throws ArithmeticException: / by zero when subLen == 0
 */
public static String[] split(String s, int subLen, boolean backwards) {
    assert s != null;
    int groups = s.length() % subLen == 0 ? s.length() / subLen : s.length() / subLen + 1;
    String[] strs = new String[groups];
    if (backwards) {
        for (int i = 0; i < groups; i++) {
            int beginIndex = s.length() - subLen * (i + 1);
            int endIndex = beginIndex + subLen;
            if (beginIndex < 0)
                beginIndex = 0;
            strs[groups - i - 1] = s.substring(beginIndex, endIndex);
        }
    } else {
        for (int i = 0; i < groups; i++) {
            int beginIndex = subLen * i;
            int endIndex = beginIndex + subLen;
            if (endIndex > s.length())
                endIndex = s.length();
            strs[i] = s.substring(beginIndex, endIndex);
        }
    }
    return strs;
}

1

次のjava 8ソリューションを使用します。

public static List<String> splitString(final String string, final int chunkSize) {
  final int numberOfChunks = (string.length() + chunkSize - 1) / chunkSize;
  return IntStream.range(0, numberOfChunks)
                  .mapToObj(index -> string.substring(index * chunkSize, Math.min((index + 1) * chunkSize, string.length())))
                  .collect(toList());
}

0

Java 8ソリューション(このように、少し簡単です):

public static List<String> partition(String string, int partSize) {
  List<String> parts = IntStream.range(0, string.length() / partSize)
    .mapToObj(i -> string.substring(i * partSize, (i + 1) * partSize))
    .collect(toList());
  if ((string.length() % partSize) != 0)
    parts.add(string.substring(string.length() / partSize * partSize));
  return parts;
}

-1

承認されたソリューションに対するコメントで@Alan Mooreに、改行付きの文字列をどのように処理できるかを尋ねました。彼はDOTALLの使用を提案しました。

彼の提案を使用して、それがどのように機能するかの小さなサンプルを作成しました:

public void regexDotAllExample() throws UnsupportedEncodingException {
    final String input = "The\nquick\nbrown\r\nfox\rjumps";
    final String regex = "(?<=\\G.{4})";

    Pattern splitByLengthPattern;
    String[] split;

    splitByLengthPattern = Pattern.compile(regex);
    split = splitByLengthPattern.split(input);
    System.out.println("---- Without DOTALL ----");
    for (int i = 0; i < split.length; i++) {
        byte[] s = split[i].getBytes("utf-8");
        System.out.println("[Idx: "+i+", length: "+s.length+"] - " + s);
    }
    /* Output is a single entry longer than the desired split size:
    ---- Without DOTALL ----
    [Idx: 0, length: 26] - [B@17cdc4a5
     */


    //DOTALL suggested in Alan Moores comment on SO: https://stackoverflow.com/a/3761521/1237974
    splitByLengthPattern = Pattern.compile(regex, Pattern.DOTALL);
    split = splitByLengthPattern.split(input);
    System.out.println("---- With DOTALL ----");
    for (int i = 0; i < split.length; i++) {
        byte[] s = split[i].getBytes("utf-8");
        System.out.println("[Idx: "+i+", length: "+s.length+"] - " + s);
    }
    /* Output is as desired 7 entries with each entry having a max length of 4:
    ---- With DOTALL ----
    [Idx: 0, length: 4] - [B@77b22abc
    [Idx: 1, length: 4] - [B@5213da08
    [Idx: 2, length: 4] - [B@154f6d51
    [Idx: 3, length: 4] - [B@1191ebc5
    [Idx: 4, length: 4] - [B@30ddb86
    [Idx: 5, length: 4] - [B@2c73bfb
    [Idx: 6, length: 2] - [B@6632dd29
     */

}

しかし、私はhttps://stackoverflow.com/a/3760193/1237974の @Jon Skeetsソリューションも好きです。誰もが正規表現で等しく経験されていない大規模なプロジェクトでの保守性のために、おそらくJonsソリューションを使用します。


-1

別のブルートフォースソリューションは、

    String input = "thequickbrownfoxjumps";
    int n = input.length()/4;
    String[] num = new String[n];

    for(int i = 0, x=0, y=4; i<n; i++){
    num[i]  = input.substring(x,y);
    x += 4;
    y += 4;
    System.out.println(num[i]);
    }

コードが部分文字列を含む文字列をステップスルーする場合


-1
    import static java.lang.System.exit;
   import java.util.Scanner;
   import Java.util.Arrays.*;


 public class string123 {

public static void main(String[] args) {


  Scanner sc=new Scanner(System.in);
    System.out.println("Enter String");
    String r=sc.nextLine();
    String[] s=new String[10];
    int len=r.length();
       System.out.println("Enter length Of Sub-string");
    int l=sc.nextInt();
    int last;
    int f=0;
    for(int i=0;;i++){
        last=(f+l);
            if((last)>=len) last=len;
        s[i]=r.substring(f,last);
     // System.out.println(s[i]);

      if (last==len)break;
       f=(f+l);
    } 
    System.out.print(Arrays.tostring(s));
    }}

結果

 Enter String
 Thequickbrownfoxjumps
 Enter length Of Sub-string
 4

 ["Theq","uick","brow","nfox","jump","s"]

-1
@Test
public void regexSplit() {
    String source = "Thequickbrownfoxjumps";
    // define matcher, any char, min length 1, max length 4
    Matcher matcher = Pattern.compile(".{1,4}").matcher(source);
    List<String> result = new ArrayList<>();
    while (matcher.find()) {
        result.add(source.substring(matcher.start(), matcher.end()));
    }
    String[] expected = {"Theq", "uick", "brow", "nfox", "jump", "s"};
    assertArrayEquals(result.toArray(), expected);
}

-1

これがRegExとJava 8ストリームに基づく私のバージョンです。このMatcher.results()メソッドはJava 9以降で使用できることに言及する価値があります。

テストが含まれています。

public static List<String> splitString(String input, int splitSize) {
    Matcher matcher = Pattern.compile("(?:(.{" + splitSize + "}))+?").matcher(input);
    return matcher.results().map(MatchResult::group).collect(Collectors.toList());
}

@Test
public void shouldSplitStringToEqualLengthParts() {
    String anyValidString = "Split me equally!";
    String[] expectedTokens2 = {"Sp", "li", "t ", "me", " e", "qu", "al", "ly"};
    String[] expectedTokens3 = {"Spl", "it ", "me ", "equ", "all"};

    Assert.assertArrayEquals(expectedTokens2, splitString(anyValidString, 2).toArray());
    Assert.assertArrayEquals(expectedTokens3, splitString(anyValidString, 3).toArray());
}

-1
public static String[] split(String input, int length) throws IllegalArgumentException {

    if(length == 0 || input == null)
        return new String[0];

    int lengthD = length * 2;

    int size = input.length();
    if(size == 0)
        return new String[0];

    int rep = (int) Math.ceil(size * 1d / length);

    ByteArrayInputStream stream = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_16LE));

    String[] out = new String[rep];
    byte[]  buf = new byte[lengthD];

    int d = 0;
    for (int i = 0; i < rep; i++) {

        try {
            d = stream.read(buf);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if(d != lengthD)
        {
            out[i] = new String(buf,0,d, StandardCharsets.UTF_16LE);
            continue;
        }

        out[i] = new String(buf, StandardCharsets.UTF_16LE);
    }
    return out;
}

-1
public static List<String> getSplittedString(String stringtoSplit,
            int length) {

        List<String> returnStringList = new ArrayList<String>(
                (stringtoSplit.length() + length - 1) / length);

        for (int start = 0; start < stringtoSplit.length(); start += length) {
            returnStringList.add(stringtoSplit.substring(start,
                    Math.min(stringtoSplit.length(), start + length)));
        }

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