¿Por qué usar LINQ o expresiones regulares cuando las funciones de manipulación de cadenas estándar de C # pueden hacer esto con menos esfuerzo y más velocidad? Además, ¿qué sucede si la cadena tiene un número impar de caracteres de longitud?
Ian Kemp
77
"Me gustaría evitar bucles", ¿por qué?
Mitch Wheat
12
Usar un bucle simple es definitivamente lo que brinda el mejor rendimiento.
Guffa
44
nichesoftware.co.nz/blog/200909/linq-vs-loop-performance es una comparación bastante buena entre linq y el bucle real sobre una matriz. Dudo que alguna vez encuentre linq más rápido que el código escrito manualmente porque sigue llamando a delegados en tiempo de ejecución que son difíciles de optimizar. Sin embargo, Linq es más divertido :)
Blindy
2
Ya sea que esté utilizando LINQ o expresiones regulares, el bucle sigue ahí.
Tenga en cuenta que es posible que se requiera código adicional para manejar con gracia los casos extremos ( nullo cadena de entrada vacía chunkSize == 0, longitud de cadena de entrada no divisible por chunkSize, etc.). La pregunta original no especifica ningún requisito para estos casos extremos y en la vida real los requisitos pueden variar, por lo que están fuera del alcance de esta respuesta.
@ Harry Buena captura! Esto puede remediarse con una expresión ternaria directa en el parámetro de recuento de la subcadena. Algo así como: (i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize. Un problema adicional es que esta función no tiene en cuenta que str sea nulo. Esto puede remediarse envolviendo toda la instrucción de retorno en otra expresión ternaria: (str != null) ? ... : Enumerable.Empty<String>();.
Drew Spickes
77
Esto estuvo cerca, pero a diferencia de los 30 votos anteriores, tuve que cambiar el límite de conteo de bucle de Rango de str.Length / chunkSizeadouble length = str.Length; double size = chunkSize; int count = (int)Math.Ceiling(length/size); return Enumerable.Range(0, count)...
gap
44
@ KonstantinSpirin Estoy de acuerdo si el código funcionó. Solo maneja el caso donde una cadena es un múltiplo de chunkSize, el resto de la cadena se pierde. Por favor, enmiende. También tenga en cuenta que LINQ y su magia no es tan fácil de entender para alguien que solo quiere buscar una solución a este problema. Una persona ahora debe comprender lo que hacen las funciones Enumerable.Range () y .Select (). No voy a argumentar que debe comprenderlo para escribir código C # / .NET ya que estas funciones han estado en el BCL durante muchos años.
CodeMonkeyKing
66
El iniciador del tema dijo en comentarios que StringLength % 4 will always be 0. Si Linqno es tan fácil de entender, existen otras respuestas que usan bucles y rendimientos. Cualquiera es libre de elegir la solución que más le guste. Puede publicar su código como respuesta y la gente felizmente votará por él.
Konstantin Spirin
3
Enumerable.Range (0, (str.Length + chunkSize - 1) / chunkSize) .Select (i => str.Substring (i * chunkSize, Math.Min (str.Length - i * chunkSize, chunkSize)))
Sten Petrov
135
En una combinación de las respuestas de dove + Konstatin ...
staticIEnumerable<string>WholeChunks(string str,int chunkSize){for(int i =0; i < str.Length; i += chunkSize)yieldreturn str.Substring(i, chunkSize);}
Esto funcionará para todas las cadenas que se pueden dividir en un número entero de fragmentos, y de lo contrario generará una excepción.
Si desea admitir cadenas de cualquier longitud, puede usar el siguiente código:
staticIEnumerable<string>ChunksUpto(string str,int maxChunkSize){for(int i =0; i < str.Length; i += maxChunkSize)yieldreturn str.Substring(i,Math.Min(maxChunkSize, str.Length-i));}
Sin embargo, el OP declaró explícitamente que no necesita esto; es algo más largo y más difícil de leer, un poco más lento. En el espíritu de KISS y YAGNI, elegiría la primera opción: probablemente sea la implementación más eficiente posible, y es muy corta, legible y, lo que es más importante, arroja una excepción para la entrada no conforme.
+1 vale la pena asentir. un poco golpea el clavo sobre la cabeza. está buscando una sintaxis sucinta y también le está dando (probablemente) un mejor rendimiento.
paloma
77
Y si lo hace "estático ... Chunk (esta cadena str, int chunkSize) {" incluso tiene uno más "nuevo" C # -Feature en él. Luego puede escribir "1111222233334444" .Chunk (4).
MartinStettner
1
@ MartininStettner: Esa es ciertamente una idea decente si esta es una operación común.
Eamon Nerbonne
Solo debe incluir el último código. El primero requiere que comprenda y pruebe que la cadena sea un múltiplo del tamaño de fragmento antes de usarla, o que comprenda que no devolverá el resto de la cadena.
CodeMonkeyKing
La pregunta del OP no deja en claro si necesita esa funcionalidad. La primera solución es más simple, más rápida y confiable, con una excepción si la cadena no se puede dividir uniformemente en el tamaño de fragmento especificado. Estoy de acuerdo en que devolver resultados "incorrectos" sería malo, pero eso no es lo que hace, solo arroja una excepción, por lo que estaría bien usarlo si puede vivir con la limitación.
Eamon Nerbonne
56
¿Por qué no bucles? Aquí hay algo que lo haría bastante bien:
string str ="111122223333444455";int chunkSize =4;int stringLength = str.Length;for(int i =0; i < stringLength ; i += chunkSize){if(i + chunkSize > stringLength) chunkSize = stringLength - i;Console.WriteLine(str.Substring(i, chunkSize));}Console.ReadLine();
No sé cómo lidiarías con el caso en el que la cadena no es un factor de 4, pero no digo que tu idea no sea posible, solo me pregunto la motivación si un bucle simple lo hace muy bien. Obviamente, lo anterior podría limpiarse e incluso ponerse como un método de extensión.
O como se menciona en los comentarios, ya sabes que es / 4
str ="1111222233334444";for(int i =0; i < stringLength; i += chunkSize){Console.WriteLine(str.Substring(i, chunkSize));}
Puede tirar int chunkSize = 4fuera del bucle. Solo se modificará en el pase final.
John Feminella
+1 para una solución simple y efectiva: así es como lo habría hecho, aunque en su lugar lo hubiera usado i += chunkSize.
Ian Kemp
Probablemente una objeción menor, pero probablemente también deberías sacarla str.Lengthdel bucle y ponerla en una variable local. El optimizador de C # puede alinear la longitud de la matriz, pero creo que el código tal como está escrito hará una llamada al método en cada bucle, lo que no es eficiente, ya que el tamaño de strnunca cambia.
Daniel Pryden
@Daniel, pon tu idea ahí. aunque no estoy seguro de que esto no se calcule en tiempo de ejecución, pero esa es otra pregunta;)
zambulló el
@Daniel volviendo a esto, bastante seguro de que esta optimización sería extraída por el compilador.
paloma
41
Usando expresiones regulares y Linq :
List<string> groups =(fromMatch m inRegex.Matches(str,@"\d{4}")select m.Value).ToList();
Creo que esto es más legible, pero es solo una opinión personal. También puede ser de una sola línea:).
Cambie el patrón a @ "\ d {1,4}" y funciona para cualquier longitud de cadena. :)
Guffa
3
+1 Aunque esto es más lento que las otras soluciones, definitivamente es muy legible. No me queda claro si el OP requiere dígitos o caracteres arbitrarios; probablemente sería prudente reemplazar la \dclase de caracteres con a .y especificar RegexOptions.Singleline.
Eamon Nerbonne
2
o simplemente Regex.Matches (s, @ "\ d {1,4}"). Seleccione (m => m.Value) .ToList (); Nunca entendí el objetivo de esta sintaxis alternativa que solo sirve para ofuscar que estamos usando métodos de extensión.
El Dag
38
Esto se basa en la solución @dove pero se implementa como un método de extensión.
Beneficios:
Método de extensión
Cubre cajas de esquina
Divide la cadena con cualquier carácter: números, letras, otros símbolos.
Código
publicstaticclassEnumerableEx{publicstaticIEnumerable<string>SplitBy(thisstring str,int chunkLength){if(String.IsNullOrEmpty(str))thrownewArgumentException();if(chunkLength <1)thrownewArgumentException();for(int i =0; i < str.Length; i += chunkLength){if(chunkLength + i > str.Length)
chunkLength = str.Length- i;yieldreturn str.Substring(i, chunkLength);}}}
Uso
var result ="bobjoecat".SplitBy(3);// bob, joe, cat
Solución interesante, pero en aras de evitar verificaciones más allá de nulo en la entrada, parece más lógico permitir que una cadena vacía solo devuelva una sola parte de cadena vacía:if (str.Length == 0) yield return String.Empty; else { for... }
Nyerguds
Quiero decir, así es como el String.Split normal maneja cadenas vacías; devuelve una entrada de cadena vacía.
Nyerguds
Nota al margen: su ejemplo de uso es incorrecto. No puede simplemente convertir IEnumerablea matriz, especialmente no implícitamente.
Nyerguds
Personalmente me gusta llamar a ese método Chunkify... No es mío, no recuerdo dónde he visto ese nombre, pero me pareció muy agradable
Quetzalcoatl
20
¿Cómo es esto para una sola línea?
List<string> result =newList<string>(Regex.Split(target,@"(?<=\G.{4})",RegexOptions.Singleline));
Con esta expresión regular no importa si el último fragmento tiene menos de cuatro caracteres, ya que solo mira a los personajes detrás de él.
Estoy seguro de que esta no es la solución más eficiente, pero solo tuve que tirarla.
en caso de target.Lenght % ChunckSize == 0que devuelva una fila vacía adicional, por ejemploList<string> result = new List<string>(Regex.Split("fooo", @"(?<=\G.{4})", RegexOptions.Singleline));
fubo
9
No es bonito ni rápido, pero funciona, es de una sola línea y es LINQy:
List<string> a = text.Select((c, i)=>new{Char= c,Index= i }).GroupBy(o => o.Index/4).Select(g =>newString(g.Select(o => o.Char).ToArray())).ToList();
¿Se garantiza que GroupBy conserva el orden de los elementos?
Konstantin Spirin
ToCharArrayEs innecesario ya que stringes IEnumerable<char>.
juharr
8
Recientemente tuve que escribir algo que logre esto en el trabajo, así que pensé en publicar mi solución a este problema. Como una ventaja adicional, la funcionalidad de esta solución proporciona una manera de dividir la cadena en la dirección opuesta y maneja correctamente los caracteres unicode como se mencionó anteriormente por Marvin Pinto anteriormente. Asi que aqui esta:
using System;
using Extensions;
namespace TestCSharp{classProgram{staticvoidMain(string[] args){string asciiStr ="This is a string.";string unicodeStr ="これは文字列です。";string[] array1 = asciiStr.Split(4);string[] array2 = asciiStr.Split(-4);string[] array3 = asciiStr.Split(7);string[] array4 = asciiStr.Split(-7);string[] array5 = unicodeStr.Split(5);string[] array6 = unicodeStr.Split(-5);}}}
namespace Extensions{publicstaticclassStringExtensions{/// <summary>Returns a string array that contains the substrings in this string that are seperated a given fixed length.</summary>/// <param name="s">This string object.</param>/// <param name="length">Size of each substring./// <para>CASE: length > 0 , RESULT: String is split from left to right.</para>/// <para>CASE: length == 0 , RESULT: String is returned as the only entry in the array.</para>/// <para>CASE: length < 0 , RESULT: String is split from right to left.</para>/// </param>/// <returns>String array that has been split into substrings of equal length.</returns>/// <example>/// <code>/// string s = "1234567890";/// string[] a = s.Split(4); // a == { "1234", "5678", "90" }/// </code>/// </example> publicstaticstring[]Split(thisstring s,int length){System.Globalization.StringInfo str =newSystem.Globalization.StringInfo(s);int lengthAbs =Math.Abs(length);if(str ==null|| str.LengthInTextElements==0|| lengthAbs ==0|| str.LengthInTextElements<= lengthAbs)returnnewstring[]{ str.ToString()};string[]array=newstring[(str.LengthInTextElements% lengthAbs ==0? str.LengthInTextElements/ lengthAbs:(str.LengthInTextElements/ lengthAbs)+1)];if(length >0)for(int iStr =0, iArray =0; iStr < str.LengthInTextElements&& iArray <array.Length; iStr += lengthAbs, iArray++)array[iArray]= str.SubstringByTextElements(iStr,(str.LengthInTextElements- iStr < lengthAbs ? str.LengthInTextElements- iStr : lengthAbs));else// if (length < 0)for(int iStr = str.LengthInTextElements-1, iArray =array.Length-1; iStr >=0&& iArray >=0; iStr -= lengthAbs, iArray--)array[iArray]= str.SubstringByTextElements((iStr - lengthAbs <0?0: iStr - lengthAbs +1),(iStr - lengthAbs <0? iStr +1: lengthAbs));returnarray;}}}
Noté un problema con este código. Usted tiene {str.ToString()}al final de su primer estado de cuenta IF. ¿Estás seguro de que no quisiste decir str.String? Tuve un problema con el código anterior, hice ese cambio y todo funcionó.
gunr2171
@ gunr2171 Parece que si str == null, esa línea también dará una NullReferenceException.
John Zabroski
5
Esto debería ser mucho más rápido y más eficiente que usar LINQ u otros enfoques utilizados aquí.
publicstaticIEnumerable<string>Splice(thisstring s,int spliceLength){if(s ==null)thrownewArgumentNullException("s");if(spliceLength <1)thrownewArgumentOutOfRangeException("spliceLength");if(s.Length==0)yieldbreak;var start =0;for(var end = spliceLength; end < s.Length; end += spliceLength){yieldreturn s.Substring(start, spliceLength);
start = end;}yieldreturn s.Substring(start);}
Este aspecto , como lo hace la comprobación temprana, pero no es así. No recibirá un error hasta que comience a enumerar lo enumerable. Debe dividir su función en dos partes, donde la primera parte verifica los argumentos y luego devuelve los resultados de la segunda parte privada que realiza la enumeración.
ErikE
4
publicstaticIEnumerable<IEnumerable<T>>SplitEvery<T>(thisIEnumerable<T> values,int n){var ls = values.Take(n);var rs = values.Skip(n);return ls.Any()?Cons(ls,SplitEvery(rs, n)):Enumerable.Empty<IEnumerable<T>>();}publicstaticIEnumerable<T>Cons<T>(T x,IEnumerable<T> xs){yieldreturn x;foreach(var xi in xs)yieldreturn xi;}
Esto devolverá 4 trozos para la cadena "1111222233334444". Si la longitud de la cadena es menor o igual que el tamaño del fragmentoBatch , devolverá la cadena como el único elemento deIEnumerable<string>
Para salida:
foreach(var chunk in chunks){Console.WriteLine(chunk);}
using System;
using System.Collections.Generic;
using System.Linq;publicclassProgram{publicstaticvoidMain(){var x ="Hello World";foreach(var i in x.ChunkString(2))Console.WriteLine(i);}}publicstaticclassExt{publicstaticIEnumerable<string>ChunkString(thisstring val,int chunkSize){return val.Select((x,i)=>new{Index= i,Value= x}).GroupBy(x => x.Index/chunkSize, x => x.Value).Select(x =>string.Join("",x));}}
Console.WriteLine(string.Join(" ","abc".Split(2,false)));// ab cConsole.WriteLine(string.Join(" ","abc".Split(2,true)));// a bcConsole.WriteLine(string.Join(" ","a".Split(2,true)));// aConsole.WriteLine(string.Join(" ","a".Split(2,false)));// a
¿Qué pasa con el caso de borde "input is a empty string"? Esperaría que, al igual que con Split, devuelva un IEnumerable con una sola entrada que contenga una cadena vacía.
Nyerguds
3
Simple y corto:
// this means match a space or not a space (anything) up to 4 charactersvar lines =Regex.Matches(str,@"[\s\S]{0,4}").Cast<Match>().Select(x => x.Value);
Maneja correctamente la longitud de la cadena de entrada no divisible por chunkSize.
Tenga en cuenta que es posible que se requiera código adicional para manejar correctamente los casos extremos (cadena de entrada nula o vacía, chunkSize == 0).
Un consejo importante si la cadena que se está fragmentando debe admitir todos los caracteres Unicode.
Si la cadena es compatible con caracteres internacionales como 𠀋, entonces divídala usando la clase System.Globalization.StringInfo. Con StringInfo, puede dividir la cadena en función del número de elementos de texto.
string internationalString ='𠀋';
La cadena anterior tiene una longitud de 2, porque la String.Lengthpropiedad devuelve el número de objetos Char en este caso, no el número de caracteres Unicode.
string originalString ="1111222233334444";List<string> test =newList<string>();int chunkSize =4;// change 4 with the size of strings you want.for(int i =0; i < originalString.Length; i = i + chunkSize){if(originalString.Length- i >= chunkSize)
test.Add(originalString.Substring(i, chunkSize));else
test.Add(originalString.Substring(i,((originalString.Length- i))));}
Calcular la longitud en la última línea es redundante, simplemente use la Substringsobrecarga que no requiere el parámetro de longitud originalString.Substring(i). También puede usar en >lugar de >=en su cheque.
Racil Hilan
@RacilHilan Probaré los cambios de código con su sugerencia y actualizaré la respuesta. Me alegra que alguien con tan buena reputación haya tenido tiempo de revisar mi código. :) Gracias, Sandeep
Sandeep Kushwah
2
Personalmente prefiero mi solución :-)
Lo maneja:
Longitudes de cadena que son múltiplos del tamaño del fragmento.
Longitudes de cadena que NO son múltiplos del tamaño del fragmento.
Longitudes de cadena que son más pequeñas que el tamaño del fragmento.
NULL y cadenas vacías (produce una excepción).
Tamaños de trozos más pequeños que 1 (produce una excepción).
Se implementa como un método de extensión y calcula de antemano el número de fragmentos que se generarán. Comprueba el último fragmento porque, en caso de que la longitud del texto no sea múltiple, debe ser más corta. Limpio, corto, fácil de entender ... ¡y funciona!
publicstaticstring[]Split(thisstringvalue,int chunkSize){if(string.IsNullOrEmpty(value))thrownewArgumentException("The string cannot be null.");if(chunkSize <1)thrownewArgumentException("The chunk size should be equal or greater than one.");int remainder;int divResult =Math.DivRem(value.Length, chunkSize,out remainder);int numberOfChunks = remainder >0? divResult +1: divResult;var result =newstring[numberOfChunks];int i =0;while(i < numberOfChunks -1){
result[i]=value.Substring(i * chunkSize, chunkSize);
i++;}int lastChunkSize = remainder >0? remainder : chunkSize;
result[i]=value.Substring(i * chunkSize, lastChunkSize);return result;}
Me gusta mucho esta respuesta, pero tal vez debería usar if ((i + 1) * chunk> = input.Length) en lugar de try / catch, ya que las excepciones son para casos excepcionales.
nelsontruran
2
Creo que esta es una respuesta directa:
publicstaticIEnumerable<string>Split(thisstring str,int chunkSize){if(string.IsNullOrEmpty(str)|| chunkSize<1)thrownewArgumentException("String can not be null or empty and chunk size should be greater than zero.");var chunkCount = str.Length/ chunkSize +(str.Length% chunkSize !=0?1:0);for(var i =0; i < chunkCount; i++){var startIndex = i * chunkSize;if(startIndex + chunkSize >= str.Length)yieldreturn str.Substring(startIndex);elseyieldreturn str.Substring(startIndex, chunkSize);}}
Sé que la pregunta tiene años, pero aquí hay una implementación de Rx. Maneja el length % chunkSize != 0problema fuera de la caja:
publicstaticIEnumerable<string>Chunkify(thisstring input,int size){if(size <1)thrownewArgumentException("size must be greater than 0");return input.ToCharArray().ToObservable().Buffer(size).Select(x =>newstring(x.ToArray())).ToEnumerable();}
He acumulado un poco en la solución de João. Lo que he hecho de manera diferente es que, en mi método, puede especificar si desea devolver la matriz con los caracteres restantes o si desea truncarlos si los caracteres finales no coinciden con la longitud de fragmento requerida, creo que es bastante flexible y el el código es bastante sencillo:
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace SplitFunction{classProgram{staticvoidMain(string[] args){string text ="hello, how are you doing today?";string[] chunks =SplitIntoChunks(text,3,false);if(chunks !=null){
chunks.ToList().ForEach(e =>Console.WriteLine(e));}Console.ReadKey();}privatestaticstring[]SplitIntoChunks(string text,int chunkSize,bool truncateRemaining){string chunk = chunkSize.ToString();string pattern = truncateRemaining ?".{"+ chunk +"}":".{1,"+ chunk +"}";string[] chunks =null;if(chunkSize >0&&!String.IsNullOrEmpty(text))
chunks =(fromMatch m inRegex.Matches(text,pattern)select m.Value).ToArray();return chunks;}}}
publicstaticList<string>SplitByMaxLength(thisstring str){List<string> splitString =newList<string>();for(int index =0; index < str.Length; index +=MaxLength){
splitString.Add(str.Substring(index,Math.Min(MaxLength, str.Length- index)));}return splitString;}
No estoy seguro de que veo el uso de back-casting que Lista IEnumerable; todo lo que hace es ocultar funciones específicas de la Lista que tal vez quieras usar. No hay ningún inconveniente en solo devolver el List.
Nyerguds
1
No recuerdo quién me dio esto, pero funciona muy bien. Probé varias formas de dividir Enumerable tipos en grupos. El uso sería así ...
classStringHelper{staticvoidMain(string[] args){string str ="Hi my name is vikas bansal and my email id is [email protected]";int offSet =10;List<string> chunks = chunkMyStr(str, offSet);Console.Read();}staticList<string> chunkMyStr(string str,int offSet){List<string> resultChunks =newList<string>();for(int i =0; i < str.Length; i += offSet){string temp = str.Substring(i,(str.Length- i)> offSet ? offSet :(str.Length- i));Console.WriteLine(temp);
resultChunks.Add(temp);}return resultChunks;}}
Respuestas:
Tenga en cuenta que es posible que se requiera código adicional para manejar con gracia los casos extremos (
null
o cadena de entrada vacíachunkSize == 0
, longitud de cadena de entrada no divisible porchunkSize
, etc.). La pregunta original no especifica ningún requisito para estos casos extremos y en la vida real los requisitos pueden variar, por lo que están fuera del alcance de esta respuesta.fuente
(i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize
. Un problema adicional es que esta función no tiene en cuenta que str sea nulo. Esto puede remediarse envolviendo toda la instrucción de retorno en otra expresión ternaria:(str != null) ? ... : Enumerable.Empty<String>();
.str.Length / chunkSize
adouble length = str.Length; double size = chunkSize; int count = (int)Math.Ceiling(length/size); return Enumerable.Range(0, count)...
StringLength % 4 will always be 0
. SiLinq
no es tan fácil de entender, existen otras respuestas que usan bucles y rendimientos. Cualquiera es libre de elegir la solución que más le guste. Puede publicar su código como respuesta y la gente felizmente votará por él.En una combinación de las respuestas de dove + Konstatin ...
Esto funcionará para todas las cadenas que se pueden dividir en un número entero de fragmentos, y de lo contrario generará una excepción.
Si desea admitir cadenas de cualquier longitud, puede usar el siguiente código:
Sin embargo, el OP declaró explícitamente que no necesita esto; es algo más largo y más difícil de leer, un poco más lento. En el espíritu de KISS y YAGNI, elegiría la primera opción: probablemente sea la implementación más eficiente posible, y es muy corta, legible y, lo que es más importante, arroja una excepción para la entrada no conforme.
fuente
¿Por qué no bucles? Aquí hay algo que lo haría bastante bien:
No sé cómo lidiarías con el caso en el que la cadena no es un factor de 4, pero no digo que tu idea no sea posible, solo me pregunto la motivación si un bucle simple lo hace muy bien. Obviamente, lo anterior podría limpiarse e incluso ponerse como un método de extensión.
O como se menciona en los comentarios, ya sabes que es / 4
fuente
int chunkSize = 4
fuera del bucle. Solo se modificará en el pase final.i += chunkSize
.str.Length
del bucle y ponerla en una variable local. El optimizador de C # puede alinear la longitud de la matriz, pero creo que el código tal como está escrito hará una llamada al método en cada bucle, lo que no es eficiente, ya que el tamaño destr
nunca cambia.Usando expresiones regulares y Linq :
Creo que esto es más legible, pero es solo una opinión personal. También puede ser de una sola línea:).
fuente
\d
clase de caracteres con a.
y especificarRegexOptions.Singleline
.Esto se basa en la solución @dove pero se implementa como un método de extensión.
Beneficios:
Código
Uso
Pruebas unitarias eliminadas por brevedad (ver revisión anterior )
fuente
if (str.Length == 0) yield return String.Empty; else { for... }
IEnumerable
a matriz, especialmente no implícitamente.Chunkify
... No es mío, no recuerdo dónde he visto ese nombre, pero me pareció muy agradable¿Cómo es esto para una sola línea?
Con esta expresión regular no importa si el último fragmento tiene menos de cuatro caracteres, ya que solo mira a los personajes detrás de él.
Estoy seguro de que esta no es la solución más eficiente, pero solo tuve que tirarla.
fuente
target.Lenght % ChunckSize == 0
que devuelva una fila vacía adicional, por ejemploList<string> result = new List<string>(Regex.Split("fooo", @"(?<=\G.{4})", RegexOptions.Singleline));
No es bonito ni rápido, pero funciona, es de una sola línea y es LINQy:
fuente
ToCharArray
Es innecesario ya questring
esIEnumerable<char>
.Recientemente tuve que escribir algo que logre esto en el trabajo, así que pensé en publicar mi solución a este problema. Como una ventaja adicional, la funcionalidad de esta solución proporciona una manera de dividir la cadena en la dirección opuesta y maneja correctamente los caracteres unicode como se mencionó anteriormente por Marvin Pinto anteriormente. Asi que aqui esta:
Además, aquí hay un enlace de imagen a los resultados de ejecutar este código: http://i.imgur.com/16Iih.png
fuente
{str.ToString()}
al final de su primer estado de cuenta IF. ¿Estás seguro de que no quisiste decirstr.String
? Tuve un problema con el código anterior, hice ese cambio y todo funcionó.Esto debería ser mucho más rápido y más eficiente que usar LINQ u otros enfoques utilizados aquí.
fuente
fuente
Puedes usar morelinq de Jon Skeet. Usar lote como:
Esto devolverá 4 trozos para la cadena
"1111222233334444"
. Si la longitud de la cadena es menor o igual que el tamaño del fragmentoBatch
, devolverá la cadena como el único elemento deIEnumerable<string>
Para salida:
y dará:
fuente
y otro enfoque:
fuente
Seis años después o_O
Simplemente porque
o
AFAIK se manejan todos los casos de borde.
fuente
Simple y corto:
fuente
.
?Maneja correctamente la longitud de la cadena de entrada no divisible por chunkSize.
Tenga en cuenta que es posible que se requiera código adicional para manejar correctamente los casos extremos (cadena de entrada nula o vacía, chunkSize == 0).
fuente
Un consejo importante si la cadena que se está fragmentando debe admitir todos los caracteres Unicode.
Si la cadena es compatible con caracteres internacionales como
𠀋
, entonces divídala usando la clase System.Globalization.StringInfo. Con StringInfo, puede dividir la cadena en función del número de elementos de texto.La cadena anterior tiene una longitud de 2, porque la
String.Length
propiedad devuelve el número de objetos Char en este caso, no el número de caracteres Unicode.fuente
La mejor, más fácil y genérica respuesta :).
fuente
Substring
sobrecarga que no requiere el parámetro de longitudoriginalString.Substring(i)
. También puede usar en>
lugar de>=
en su cheque.Personalmente prefiero mi solución :-)
Lo maneja:
Se implementa como un método de extensión y calcula de antemano el número de fragmentos que se generarán. Comprueba el último fragmento porque, en caso de que la longitud del texto no sea múltiple, debe ser más corta. Limpio, corto, fácil de entender ... ¡y funciona!
fuente
fuente
Creo que esta es una respuesta directa:
Y cubre casos extremos.
fuente
Sé que la pregunta tiene años, pero aquí hay una implementación de Rx. Maneja el
length % chunkSize != 0
problema fuera de la caja:fuente
He acumulado un poco en la solución de João. Lo que he hecho de manera diferente es que, en mi método, puede especificar si desea devolver la matriz con los caracteres restantes o si desea truncarlos si los caracteres finales no coinciden con la longitud de fragmento requerida, creo que es bastante flexible y el el código es bastante sencillo:
fuente
fuente
Se modificó ligeramente para devolver partes cuyo tamaño no es igual al fragmento
fuente
List
aIEnumerable
; todo lo que hace es ocultar funciones específicas de la Lista que tal vez quieras usar. No hay ningún inconveniente en solo devolver elList
.No recuerdo quién me dio esto, pero funciona muy bien. Probé varias formas de dividir Enumerable tipos en grupos. El uso sería así ...
El código de extensión se vería así ...
fuente
fuente
i += offSet
a sufor
expresión.Modificado (ahora se acepta cualquier nula no
string
y cualquier positivochunkSize
) Konstantin Spirin solución 's:Pruebas:
fuente
manifestación
fuente
Basado en otras respuestas de carteles, junto con algunas muestras de uso:
fuente
Usando las extensiones de Buffer de la biblioteca IX
fuente