2010-09-21 13 views
83

Cómo dividir la cadena "Thequickbrownfoxjumps" en subcadenas de igual tamaño en Java. Eg. "Thequickbrownfoxjumps" de 4 igual tamaño debe dar la salida.Cadena de división para subcadenas de igual longitud en Java

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

pregunta similar:

Split string into equal-length substrings in Scala

+3

¿Qué has probado? ¿Por qué eso no funcionó? – Thilo

+2

¿Necesita usar una expresión regular para esto? Solo preguntando por la etiqueta de expresión regular ... –

+0

El enlace de @Thilo que publicó es para Scala, él pregunta por lo mismo en Java –

Respuesta

163

Aquí está la versión de expresiones regulares de una sola línea:

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

\G es una afirmación de anchura cero que coincide con la posición en la que terminó el partido anterior. Si hay era sin coincidencia previa, coincide con el comienzo de la entrada, el mismo que \A. El aspecto subyacente coincide con la posición de cuatro caracteres a lo largo del final del último partido.

Tanto lookbehind como \G son funciones avanzadas de regex, no compatibles con todos los sabores. Además, \G no se implementa consistentemente en los sabores que sí lo admiten. Este truco funcionará (por ejemplo) en Java, Perl, .NET y JGSoft, pero no en PHP (PCRE), Ruby 1.9+ o TextMate (ambos Oniguruma). El /y de JavaScript (bandera adhesiva) no es tan flexible como \G, y no se pudo usar de esta manera, incluso si JS sí admitía lookbehind.

Debo mencionar que no necesariamente recomiendo esta solución si tiene otras opciones. Las soluciones no-regex en las otras respuestas pueden ser más largas, pero también son auto-documentadas; este es solo el frente a de eso. ;)

Además, esto no funciona en Android, que no admite el uso de \G en lookbehinds.

+1

Sí, eso es lo que estaba buscando. (+1) –

+0

Aquí hay una demostración en ideone.com: http://ideone.com/oInXz –

+2

En PHP 5.2.4 funciona siguiendo el código: return preg_split ('/ (? <= \ G. {'. $ Len. '})/u', $ str, -1, PREG_SPLIT_NO_EMPTY); – Igor

3

Puede utilizar substring de String.class (manejo de excepciones) o desde Apache lang commons (que maneja excepciones para usted)

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

Póngalo dentro de un bucle y estás listo para ir.

+0

¿Qué pasa con el método 'substring' en la clase estándar' String'? – Grodriguez

+0

La versión común evita excepciones (fuera de límites y tal) – Thilo

+5

Veo; Diría que prefiero 'evitar excepciones' controlando los parámetros en el código de llamada. – Grodriguez

97

Bueno, es bastante fácil de hacer esto por la fuerza bruta:

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

no creo que es realmente vale la pena utilizar una expresión regular para esto.

EDIT: Mi razonamiento para no usar una expresión regular:

  • Esto no utilice ninguno de los concordancia con el modelo real de expresiones regulares. Solo está contando.
  • Yo sospecho lo anterior será más eficiente, aunque en la mayoría de los casos, no importará
  • Si es necesario utilizar tamaños variables en diferentes lugares, ya sea que haya conseguido la repetición o una función de ayuda para construir el Regex en sí basado en un parámetro - ick.
  • La expresión regular proporcionada en otra respuesta no compiló en primer lugar (escape no válido) y luego no funcionó. Mi código funcionó por primera vez. Eso es más un testimonio de la usabilidad de expresiones regulares vs código simple, IMO.
+0

@Jon Skeet: Gracias por despejarlo, pero no entiendo tu punto. "No creo que valga la pena usar una expresión regular para esto" –

+0

¿Por qué no vale la pena usar una expresión regular? No estoy en desacuerdo contigo. Me pregunto si es más costoso o legible, etc. – Gage

+0

@ org.life.java: Bueno, ¿cuál es el beneficio de usar una expresión regular aquí? En realidad, no estás combinando patrones como tal ... solo estás obteniendo las subcadenas a ciegas. No parece una buena opción para expresiones regulares para mí. –

4
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; 
} 
+3

Fuera de interés, ¿tiene algo en contra de 'for' loops? –

+0

Un bucle 'for' es de hecho un uso de elección más 'natural' para esto :-) Gracias por señalar esto. – Grodriguez

5
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; 
} 
+0

Dado que 'src.length()' y 'len' son ambos' int's, su llamada 'ceiling' no está logrando lo que desea; revise cómo algunas de las otras respuestas lo están haciendo: (src.length)() + len - 1)/len –

+0

@Michael: Buen punto. No lo probé con cadenas de longitudes no múltiples. Está arreglado ahora. – Saul

57

Esto es muy fácil con Google Guava:

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

Salida:

Theq 
uick 
brow 
nfox 
jump 
s 

O si usted necesita el resultado como una matriz, puede utilizar este código :

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

Referencia:

Nota: la construcción del divisor se muestra en línea de arriba, pero desde divisores son inmutables y reutilizable, que es una buena práctica para almacenar en constantes:

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

// more code 

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

Gracias por la publicación (Por informarme sobre el método de la biblioteca de guayaba). Pero tendré que aceptar la respuesta de la expresión regular http://stackoverflow.com/questions/3760152/split-string-of-equal-lengths-in- java/3761521 # 3761521 ya que no requiere ninguna biblioteca de terceros ni una sola línea. – Emil

+1

Hombre esto es realmente útil, gracias! – javamonkey79

+1

Incluir cientos de KB de código de biblioteca solo para realizar esta sencilla tarea es casi seguro que no es lo correcto. –

11

Si está utilizando guava bibliotecas de uso general de Google (y honestamente, cualquier nuevo proyecto Java probablemente debe ser), esto es increíblemente trivial con la clase Splitter:

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

y eso es es. ¡Fácil como!

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

Resultado

Enter String 
Thequickbrownfoxjumps 
Enter length Of Sub-string 
4 

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

me preguntó @Alan Moore en un comentario a la accepted solution cómo podría ser manejado con cuerdas nuevas líneas. Él sugirió usar DOTALL.

Usando su sugerencia creé una pequeña muestra de cómo funciona:

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] - [[email protected] 
    */ 


    //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] - [[email protected] 
    [Idx: 1, length: 4] - [[email protected] 
    [Idx: 2, length: 4] - [[email protected] 
    [Idx: 3, length: 4] - [[email protected] 
    [Idx: 4, length: 4] - [[email protected] 
    [Idx: 5, length: 4] - [[email protected] 
    [Idx: 6, length: 2] - [[email protected] 
    */ 

} 

Pero me gusta la solución @ Jon Skeets en https://stackoverflow.com/a/3760193/1237974 también. Para la mantenibilidad en proyectos más grandes donde no todos tienen la misma experiencia en expresiones regulares, probablemente usaría la solución de Jons.

0

Otra solución podría ser la fuerza bruta,

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

caso de que el código sólo unos pasos a través de la cadena con subseries

2

prefiero esta solución sencilla:

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

¡No hagas esto! La cadena es inmutable, por lo que su código necesita copiar toda la cadena restante cada 4 caracteres. Por lo tanto, su fragmento toma un tiempo cuadrático en lugar de lineal en el tamaño del String. – Tobias

+0

@Tobias: incluso si String fue mutable, este fragmento hace la copia redundante mencionada, excepto que hay procesos complejos de compilación relacionados. La única razón para usar este fragmento es la simplicidad del código. –

+0

¿Cambió su código desde la primera vez que lo publicó? La última versión en realidad no hace copias: substring() se ejecuta de manera eficiente (tiempo constante, al menos en las versiones anteriores de Java); guarda una referencia al carácter completo de la cadena [] (al menos en las versiones anteriores de Java), pero eso está bien en este caso, ya que mantiene todos los caracteres. Así que el último código que tienes aquí está realmente bien (módulo que tu código imprime una línea vacía si el contenido comienza como la cadena vacía, que puede no ser lo que se pretende). – Tobias

1

En caso de que desea dividir la cadena igualmente hacia atrás, es decir, de derecha a izquierda, por ejemplo, para dividir 1010001111 a [10, 1000, 1111], aquí está el código:

/** 
* @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; 
} 
0
@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); 
} 
Cuestiones relacionadas