2009-10-27 14 views
10

Quizás una pregunta básica, pero digamos que tengo una cadena de 2000 caracteres de longitud, necesito dividir esta cadena en un máximo de 512 pedazos de caracteres cada uno.División de cadena en 512 bloques de char

¿Hay una buena manera, como un bucle más o menos para hacer esto?

+0

¿Estás seguro de que necesita 512 ** char ** trozos? Porque eso es diferente de 512 ** bytes **, que es una restricción más común. –

+1

@Henk: por otro lado, dividir * text * en fragmentos basados ​​en * bytes * sería bastante extraño: los resultados dependerían de la codificación. –

+0

Jon, sí, un problema común al volver a armar el texto. Pero algunos canales de E/S operan en bloques de 512 bytes. –

Respuesta

20

Algo como esto:

private IList<string> SplitIntoChunks(string text, int chunkSize) 
{ 
    List<string> chunks = new List<string>(); 
    int offset = 0; 
    while (offset < text.Length) 
    { 
     int size = Math.Min(chunkSize, text.Length - offset); 
     chunks.Add(text.Substring(offset, size)); 
     offset += size; 
    } 
    return chunks; 
} 

O simplemente para repetir:

private IEnumerable<string> SplitIntoChunks(string text, int chunkSize) 
{ 
    int offset = 0; 
    while (offset < text.Length) 
    { 
     int size = Math.Min(chunkSize, text.Length - offset); 
     yield return text.Substring(offset, size); 
     offset += size; 
    } 
} 

Tenga en cuenta que este se divide en trozos de 16 caracteres UTF-unidades de código, que no es exactamente lo mismo que la división en trozos de puntos de código Unicode, que a su vez pueden no ser lo mismo que dividirse en trozos de glifos.

+0

dammit Jon you golpéame y utilicé tu implementación también ... –

+0

Aprendí mucho de esta. Aceptar y +1. ¡Muy agradable! – janhartmann

+0

Este algoritmo (y su _compatibilidad_ con Unicode) se ha discutido también en Revisión de código: [Divida una cadena en fragmentos de la misma longitud] (http://codereview.stackexchange.com/a/111925/13424). –

1
static IEnumerable<string> Split(string str, int chunkSize)  
{ 
    int len = str.Length; 
    return Enumerable.Range(0, len/chunkSize).Select(i => str.Substring(i * chunkSize, chunkSize));  
} 

fuente: Splitting a string into chunks of a certain size

+0

+1 para creatividad, -1 para rendimiento y legibilidad – Foxfire

+9

No proporciona el fragmento final en este caso. –

-1

Algo así?

Calculate eachLength = StringLength/WantedCharLength 
Then for (int i = 0; i < StringLength; i += eachLength) 
SubString (i, eachLength); 
+2

¿Esto es C#? .... – Abel

1

me atreveré a proporcionar una versión más LINQified de solución de Jon, basado en el hecho de que el tipo string implementa IEnumerable<char>:

private IList<string> SplitIntoChunks(string text, int chunkSize) 
{ 
    var chunks = new List<string>(); 
    int offset = 0; 
    while(offset < text.Length) { 
     chunks.Add(new string(text.Skip(offset).Take(chunkSize).ToArray())); 
     offset += chunkSize; 
    } 
    return chunks; 
} 
+1

Lo consideré, particularmente porque MoreLINQ proporciona un buen método de Partición para este tipo de cosas. Sin embargo, la eficiencia de esto sería absolutamente horrible :( –

+0

Es bueno saber que, dado que tiendo a usar LINQ para todo ... – Konamiman

+0

por cadena, no tiene un método de extensión para "Omitir" que debería hacer ToCharArray primero. –

3

mediante la aplicación de Jon y el rendimiento palabra clave.

IEnumerable<string> Chunks(string text, int chunkSize) 
{ 
    for (int offset = 0; offset < text.Length; offset += chunkSize) 
    { 
     int size = Math.Min(chunkSize, text.Length - offset); 
     yield return text.Substring(offset, size); 
    } 
} 
+0

uso interesante de para vs mi tiempo ... estoy tratando de decidir cuál es más fácil de leer. No necesitas romper el rendimiento al final, por cierto. –

+0

Me tomé la libertad de corregir el salto de rendimiento redundante cuando @Jon mencionó –

+0

gracias, @LouisRhys :) –

3

Aunque esta pregunta, mientras tanto, tiene una respuesta aceptada, aquí hay una versión corta con la ayuda de expresiones regulares. Puede que a los puristas no les guste (comprensiblemente), pero cuando necesitas una solución rápida y eres útil con las expresiones regulares, puede ser. El rendimiento es bastante bueno, sorprendentemente:

string [] split = Regex.Split(yourString, @"(?<=\G.{512})"); 

¿Qué es lo que hace? Negativo mirar hacia atrás y recordar la última posición con \G. También capturará el último bit, incluso si no es divisible por 512.

+0

. Útil para inspeccionar cadenas largas en Ventana Inmediata. – Dialecticus

+1

¿Por qué no? Es corto, rápido y claro (si conoce expresiones regulares, personalmente tuve que leerlo y probarlo 3 veces). Buena versión! –

1

La mayoría de las respuestas pueden tener el mismo defecto. Dado un texto vacío, no cederán nada. Nosotros (I) esperamos al menos recuperar esa cadena vacía (el mismo comportamiento que una división en una cadena no en la cadena, que devolverá un elemento: esa cadena dada)

así que deberíamos repetir al menos una vez todo el tiempo (basado en el código de Jon):

IEnumerable<string> SplitIntoChunks (string text, int chunkSize) 
{ 
    int offset = 0; 
    do 
    { 
     int size = Math.Min (chunkSize, text.Length - offset); 
     yield return text.Substring (offset, size); 
     offset += size; 
    } while (offset < text.Length); 
} 

o utilizando una de (Editado: después de juguetear un poco más con esto me he encontrado una mejor manera de manejar el caso chunkSize mayor que el texto):

IEnumerable<string> SplitIntoChunks (string text, int chunkSize) 
{ 
    if (text.Length <= chunkSize) 
     yield return text; 
    else 
    { 
     var chunkCount = text.Length/chunkSize; 
     var remainingSize = text.Length % chunkSize; 

     for (var offset = 0; offset < chunkCount; ++offset) 
      yield return text.Substring (offset * chunkSize, chunkSize); 

     // yield remaining text if any 
     if (remainingSize != 0) 
      yield return text.Substring (chunkCount * chunkSize, remainingSize); 
    } 
} 

que también podría ser utilizado con el do/while;)

0

Genérico método de extensión:

using System; 
using System.Collections.Generic; 
using System.Linq; 

public static class IEnumerableExtensions 
{ 
    public static IEnumerable<IEnumerable<T>> SplitToChunks<T> (this IEnumerable<T> coll, int chunkSize) 
    { 
    int skipCount = 0; 
    while (coll.Skip (skipCount).Take (chunkSize) is IEnumerable<T> part && part.Any()) 
    { 
     skipCount += chunkSize; 
     yield return part; 
    } 
    } 
} 

class Program 
{ 
    static void Main (string[] args) 
    { 
    var col = Enumerable.Range(1,1<<10); 
    var chunks = col.SplitToChunks(8); 

    foreach (var c in chunks.Take (200)) 
    { 
     Console.WriteLine (string.Join (" ", c.Select (n => n.ToString ("X4")))); 
    } 

    Console.WriteLine(); 
    Console.WriteLine(); 

    "Split this text into parts that are fifteen characters in length, surrounding each part with single quotes and output each into the console on seperate lines." 
     .SplitToChunks (15) 
     .Select(p => $"'{string.Concat(p)}'") 
     .ToList() 
     .ForEach (p => Console.WriteLine (p)); 

    Console.ReadLine(); 
    } 
}