web-dev-qa-db-fra.com

Fractionner une chaîne pour obtenir des sous-chaînes de longueur égale en Java

Comment diviser la chaîne "Thequickbrownfoxjumps" en sous-chaînes de taille égale en Java . "Thequickbrownfoxjumps" de 4 taille égale devrait donner la sortie.

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

Question similaire:

Fractionner la chaîne en sous-chaînes de longueur égale dans Scala

100
Emil

Voici la version regex one-liner:

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

\G est une assertion de largeur zéro qui correspond à la position où la correspondance précédente s'est terminée. S'il n'y a pas de correspondance précédente était, le début de l'entrée est identique, à l'identique de \A. La ligne de visée ci-dessous correspond à la position de quatre caractères à partir de la fin du dernier match.

Lookbehind et \G sont des fonctionnalités regex avancées, qui ne sont pas prises en charge par toutes les variantes. De plus, \G n'est pas implémenté de manière cohérente dans les variantes qui le prennent en charge. Cette astuce fonctionnera (par exemple) dans Java , Perl, .NET et JGSoft, mais pas dans PHP (PCRE), Ruby 1.9+ ou TextMate (Oniguruma). /y (indicateur collant) de JavaScript n'est pas aussi flexible que \G et ne peut pas être utilisé de cette manière même si JS prenait en charge lookbehind.

Je devrais mentionner que je ne fais pas nécessairement recommander cette solution si vous avez d'autres options. Les solutions autres que les expressions rationnelles dans les autres réponses peuvent être plus longues, mais elles s'auto-documentent également; celui-ci est à peu près le opposé de cela. ;)

En outre, cela ne fonctionne pas sous Android, qui ne prend pas en charge l'utilisation de \G dans les lookbehind.

194
Alan Moore

Eh bien, c'est assez facile de le faire par force brute:

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

Je ne pense pas que cela vaut vraiment la peine d'utiliser une regex pour cela.

EDIT: Mon raisonnement pour ne pas utiliser une regex:

  • Cela n'utilise aucun des vrais modèles de correspondance des regex. C'est juste en train de compter.
  • Je soupçonne ce qui précède sera plus efficace, bien que dans la plupart des cas cela n'aura pas d'importance
  • Si vous devez utiliser des tailles variables à différents endroits, vous avez soit la répétition, soit une fonction d’aide pour construire l’expression rationnelle elle-même sur la base d’un paramètre - ick.
  • La regex fournie dans une autre réponse n'a pas été compilée (échappement invalide), puis n'a pas fonctionné. Mon code a fonctionné la première fois. Cela témoigne davantage de la convivialité des expressions rationnelles par rapport au code simple, IMO.
116
Jon Skeet

C'est très facile avec Google Guava :

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

Sortie:

Theq
uick
brow
nfox
jump
s

Ou si vous avez besoin du résultat sous forme de tableau, vous pouvez utiliser ce code:

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

Référence:

Remarque: La structure des séparateurs est illustrée ci-dessus, mais comme les séparateurs sont immuables et réutilisables, il est recommandé de les stocker dans des constantes:

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

// more code

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

Si vous utilisez les bibliothèques généralistes de guava de Google (et très honnêtement, tout nouveau projet Java probablement devrait être), ceci est incroyablement trivial avec la classe Splitter :

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

et c'est ça. Facile comme!

12
Cowan
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;
}
7
Saul
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
Grodriguez

Vous pouvez utiliser substring depuis String.class (gestion des exceptions) ou depuis Apache lang commons (il gère les exceptions pour vous).

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

Mettez-le dans une boucle et vous êtes prêt à partir.

4
pakore

Voici une implémentation one-liner utilisant des flux 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();

Il donne la sortie suivante:

[Theq, uick, brow, nfox, jump, s]
3
Pankaj Singhal

Voici une version one-liner qui utilise Java 8 IntStream pour déterminer les index des débuts de tranche:

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);
2
Marko Previsic

Je préfère cette solution simple:

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

Si vous souhaitez scinder la chaîne de manière égale vers l’arrière, c’est-à-dire de droite à gauche, par exemple, pour scinder 1010001111 en [10, 1000, 1111], le code est le suivant:

/**
 * @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;
}
2
Ivan Huang
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;
}
0
User8461

Voici ma version basée sur les flux RegEx et Java 8. Il convient de mentionner que la méthode Matcher.results() est disponible depuis Java 9.

Test inclus.

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

Une autre solution de force brute pourrait être, 

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

Où le code passe juste à travers la chaîne avec des sous-chaînes

0
Hubbly

Solution Java 8 (comme ceci mais un peu plus simple):

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;
}
0
Timofey Gorshkov

j'utilise la solution Java 8 suivante:

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
rloeffel
@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);
}
0

J'ai demandé à @Alan Moore dans un commentaire sur la solution acceptée comment les chaînes contenant des nouvelles lignes pourraient être traitées. Il a suggéré d'utiliser DOTALL.

En utilisant sa suggestion, j'ai créé un petit échantillon de la façon dont cela fonctionne:

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

}

Mais j'aime bien la solution @Jon Skeets dans https://stackoverflow.com/a/3760193/1237974 aussi. Pour la maintenabilité dans les grands projets où tous ne sont pas également expérimentés dans les expressions régulières, j'utiliserais probablement la solution de Jons.

0
joensson
    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));
    }}

Résultat

 Enter String
 Thequickbrownfoxjumps
 Enter length Of Sub-string
 4

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