Pourquoi utiliser LINQ ou des expressions régulières alors que les fonctions standard de manipulation de chaînes de C # peuvent le faire avec moins d'effort et plus de vitesse? De plus, que se passe-t-il si la chaîne a un nombre impair de caractères?
Ian Kemp
7
"Je voudrais éviter les boucles" - pourquoi?
Mitch Wheat
12
L'utilisation d'une boucle simple est certainement ce qui donne les meilleures performances.
Guffa
4
nichesoftware.co.nz/blog/200909/linq-vs-loop-performance est une assez bonne comparaison entre linq et le bouclage réel sur un tableau. Je doute que vous trouviez jamais linq plus rapidement que du code écrit manuellement, car il continue d'appeler des délégués d'exécution difficiles à optimiser. Linq est plus amusant cependant :)
Blindy
2
Que vous utilisiez LINQ ou des expressions rationnelles, la boucle est toujours là.
Veuillez noter qu'un code supplémentaire peut être requis pour gérer avec élégance les cas marginaux ( nullou chaîne d'entrée vide chunkSize == 0,, longueur de chaîne d'entrée non divisible par chunkSize, etc.). La question d'origine ne spécifie aucune exigence pour ces cas marginaux et dans la vie réelle, les exigences peuvent varier et sont donc hors de portée de cette réponse.
@Harry Bonne prise! Cela peut être résolu avec une expression ternaire de dépôt sur le paramètre count de la sous-chaîne. Quelque chose comme: (i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize. Un problème supplémentaire est que cette fonction ne tient pas compte du fait que str est nul. Cela peut être corrigé en enveloppant toute la déclaration de retour dans une autre expression ternaire: (str != null) ? ... : Enumerable.Empty<String>();.
Drew Spickes
7
C'était proche, mais contrairement aux 30 votants précédents, j'ai dû changer la limite de comptage de boucles de Range du str.Length / chunkSizeaudouble length = str.Length; double size = chunkSize; int count = (int)Math.Ceiling(length/size); return Enumerable.Range(0, count)...
gap
4
@KonstantinSpirin Je suis d'accord si le code a fonctionné. Il ne gère que le cas où une chaîne est un multiple de chunkSize, le reste de la chaîne est perdu. Veuillez corriger. Gardez également à l'esprit que LINQ et sa magie ne sont pas aussi faciles à comprendre pour quelqu'un qui veut simplement chercher une solution à ce problème. Une personne doit maintenant comprendre ce que font les fonctions Enumerable.Range () et .Select (). Je ne dirai pas que vous devriez avoir une compréhension de cela pour écrire du code C # /. NET car ces fonctions sont dans la BCL depuis de nombreuses années maintenant.
CodeMonkeyKing
6
Le sujet a déclaré dans les commentaires que StringLength % 4 will always be 0. Si ce Linqn'est pas aussi facile à comprendre, il existe d'autres réponses qui utilisent des boucles et des rendements. Tout le monde est libre de choisir la solution qu'elle préfère. Vous pouvez poster votre code comme réponse et les gens voteront volontiers pour lui.
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
Dans une combinaison de colombe + réponses de Konstatin ...
staticIEnumerable<string>WholeChunks(string str,int chunkSize){for(int i =0; i < str.Length; i += chunkSize)yieldreturn str.Substring(i, chunkSize);}
Cela fonctionnera pour toutes les chaînes qui peuvent être divisées en un nombre entier de morceaux et lèvera une exception dans le cas contraire.
Si vous souhaitez prendre en charge des chaînes de n'importe quelle longueur, vous pouvez utiliser le code suivant:
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));}
Cependant, le PO a déclaré explicitement qu'il n'en avait pas besoin; c'est un peu plus long et plus difficile à lire, légèrement plus lent. Dans l'esprit de KISS et YAGNI, j'irais avec la première option: c'est probablement l'implémentation la plus efficace possible, et elle est très courte, lisible et, surtout, lève une exception pour les entrées non conformes.
+1 vaut un signe de tête. frappe un peu clou sur la tête. il cherche un sytnax succinct et vous donnez également (probablement) de meilleures performances.
plongé le
7
Et si vous le rendez "statique ... Chunk (cette chaîne de caractères, int chunkSize) {" vous avez même un "nouveau" C # -Feature en plus. Ensuite, vous pouvez écrire "1111222233334444". Chunk (4).
MartinStettner
1
@MartinStettner: C'est certainement une idée décente s'il s'agit d'une opération courante.
Eamon Nerbonne
Vous ne devez inclure que ce dernier code. Le premier nécessite que vous compreniez et testiez que la chaîne est un multiple de la taille du bloc avant de l'utiliser, ou comprenez qu'elle ne renverra pas le reste de la chaîne.
CodeMonkeyKing
La question du PO ne précise pas s'il a besoin de cette fonctionnalité. La première solution est plus simple, plus rapide et échoue de manière fiable à une exception si la chaîne ne peut pas être divisée uniformément en la taille de bloc spécifiée. Je suis d'accord que renvoyer de «mauvais» résultats serait mauvais, mais ce n'est pas ce qu'il fait - cela lève juste une exception, donc je serais d'accord pour l'utiliser si vous pouvez vivre avec la limitation.
Eamon Nerbonne
56
Pourquoi pas des boucles? Voici quelque chose qui le ferait très 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();
Je ne sais pas comment vous traiteriez le cas où la chaîne n'est pas un facteur de 4, mais ne dites pas que votre idée n'est pas possible, vous vous demandez simplement la motivation si une simple boucle for le fait très bien? Évidemment, ce qui précède pourrait être nettoyé et même mis en place comme méthode d'extension.
Ou comme mentionné dans les commentaires, vous savez que c'est / 4 alors
str ="1111222233334444";for(int i =0; i < stringLength; i += chunkSize){Console.WriteLine(str.Substring(i, chunkSize));}
Vous pouvez sortir int chunkSize = 4de la boucle. Il ne sera modifié que lors de la dernière passe.
John Feminella
+1 pour une solution simple et efficace - c'est ainsi que je l'aurais fait, bien que j'aurais utilisé à la i += chunkSizeplace.
Ian Kemp
Probablement un petit problème, mais vous devriez également retirer str.Lengthla boucle de la boucle et la placer dans une variable locale. L'optimiseur C # peut être en mesure d'inline la longueur du tableau, mais je pense que le code tel qu'il est écrit fera un appel de méthode sur chaque boucle, ce qui n'est pas efficace, car la taille de strne change jamais.
Daniel Pryden
@Daniel, mets ton idée dedans. bien que je ne suis pas sûr que cela ne serait pas calculé lors de l'exécution, mais c'est une autre question;)
plongé le
@Daniel y revient, à peu près sûr que cette optimisation serait extraite par le compilateur.
plongé
41
Utilisation d' expressions régulières et de Linq :
List<string> groups =(fromMatch m inRegex.Matches(str,@"\d{4}")select m.Value).ToList();
Je trouve cela plus lisible, mais ce n'est qu'une opinion personnelle. Il peut également s'agir d'un aller simple:).
Modifiez le modèle en @ "\ d {1,4}" et cela fonctionne pour n'importe quelle longueur de chaîne. :)
Guffa
3
+1 Bien que ce soit plus lent que les autres solutions, il est certainement très lisible. Il n'est pas clair pour moi si l'OP nécessite des chiffres ou des caractères arbitraires; il serait probablement judicieux de remplacer la \dclasse de caractères par a .et de spécifier RegexOptions.Singleline.
Eamon Nerbonne
2
ou simplement Regex.Matches (s, @ "\ d {1,4}"). Sélectionnez (m => m.Value) .ToList (); Je n'ai jamais compris l'intérêt de cette syntaxe alternative qui ne sert qu'à masquer que nous utilisons des méthodes d'extension.
Le Dag
38
Ceci est basé sur la solution @dove mais implémenté comme méthode d'extension.
Avantages:
Méthode d'extension
Couvre les caisses d'angle
Divise la chaîne avec n'importe quel caractère: chiffres, lettres, autres symboles
Code
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);}}}
Usage
var result ="bobjoecat".SplitBy(3);// bob, joe, cat
Solution intéressante, mais pour éviter les vérifications au-delà de null sur l'entrée, il semble plus logique de permettre à une chaîne vide de renvoyer une seule partie de chaîne vide:if (str.Length == 0) yield return String.Empty; else { for... }
Nyerguds
Je veux dire, c'est ainsi que le String.Split normal gère les chaînes vides; il renvoie une entrée de chaîne vide.
Nyerguds
Note latérale: votre exemple d'utilisation est incorrect. Vous ne pouvez pas simplement convertir IEnumerableen tableau, surtout pas implicitement.
Nyerguds
Personnellement, j'aime appeler cette méthode Chunkify.. Ce n'est pas la mienne, je ne me souviens pas où j'ai vu ce nom, mais c'était très agréable pour moi
quetzalcoatl
20
Comment est-ce pour un one-liner?
List<string> result =newList<string>(Regex.Split(target,@"(?<=\G.{4})",RegexOptions.Singleline));
Avec cette expression régulière, peu importe si le dernier morceau est inférieur à quatre caractères, car il ne regarde que les caractères derrière lui.
Je suis sûr que ce n'est pas la solution la plus efficace, mais je n'ai eu qu'à la lancer.
dans le cas où target.Lenght % ChunckSize == 0cela renvoie une ligne vide supplémentaire, par exempleList<string> result = new List<string>(Regex.Split("fooo", @"(?<=\G.{4})", RegexOptions.Singleline));
fubo
9
Ce n'est pas joli et ce n'est pas rapide, mais ça marche, c'est une doublure et c'est 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();
Est-il garanti que GroupBy préserve l'ordre des éléments?
Konstantin Spirin
ToCharArrayest inutile depuis l' stringest IEnumerable<char>.
juharr
8
J'ai récemment dû écrire quelque chose qui accomplit cela au travail, alors j'ai pensé publier ma solution à ce problème. En prime, la fonctionnalité de cette solution fournit un moyen de diviser la chaîne dans la direction opposée et elle gère correctement les caractères unicode comme mentionné précédemment par Marvin Pinto ci-dessus. Voici donc:
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;}}}
J'ai remarqué un problème avec ce code. Vous avez {str.ToString()}à la fin de votre première déclaration IF. Êtes-vous sûr de ne pas vouloir dire str.String? J'ai eu un problème avec le code ci-dessus, j'ai fait ce changement et tout a fonctionné.
gunr2171
@ gunr2171 Il semble que si str == null, cette ligne donnera également une NullReferenceException.
John Zabroski
5
Cela devrait être beaucoup plus rapide et plus efficace que l'utilisation de LINQ ou d'autres approches utilisées ici.
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);}
Cela ressemble à une vérification précoce, mais ce n'est pas le cas. Vous n'obtenez pas d'erreur tant que vous n'avez pas commencé à énumérer l'énumérable. Vous devez diviser votre fonction en deux parties, où la première partie vérifie l'argument, puis renvoie les résultats de la deuxième partie privée qui effectue l'énumération.
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;}
Cela renverra 4 morceaux pour la chaîne "1111222233334444". Si la longueur de la chaîne est inférieure ou égale à la taille du bloc Batch, la chaîne sera renvoyée comme seul élément deIEnumerable<string>
Pour la sortie:
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'en est-il du cas de bord "l'entrée est une chaîne vide"? Je m'attends à ce que, tout comme avec Split, retourne un IEnumerable avec une seule entrée contenant une chaîne vide.
Nyerguds
3
Simple et court:
// 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);
Un conseil important si la chaîne qui est fragmentée doit prendre en charge tous les caractères Unicode.
Si la chaîne doit prendre en charge les caractères internationaux comme 𠀋, divisez la chaîne à l'aide de la classe System.Globalization.StringInfo. À l'aide de StringInfo, vous pouvez fractionner la chaîne en fonction du nombre d'éléments de texte.
string internationalString ='𠀋';
La chaîne ci-dessus a une longueur de 2, car la String.Lengthpropriété renvoie le nombre d'objets Char dans cette instance, pas le nombre de caractères Unicode.
Meilleure réponse, la plus simple et la plus générique :).
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))));}
Le calcul de la longueur dans la dernière ligne est redondant, utilisez simplement la Substringsurcharge qui ne nécessite pas le paramètre de longueur originalString.Substring(i). Vous pouvez également utiliser >au lieu de >=dans votre chèque.
Racil Hilan
@RacilHilan Je vais tester les changements de code avec votre suggestion et mettre à jour la réponse. Je suis content que quelqu'un avec une si bonne réputation ait eu le temps de revoir mon code. :) Merci, Sandeep
Sandeep Kushwah
2
Personnellement je préfère ma solution :-)
Il gère:
Longueurs de chaîne qui sont un multiple de la taille du bloc.
Longueurs de chaîne qui ne sont PAS un multiple de la taille de bloc.
Longueurs de chaîne inférieures à la taille de bloc.
Chaînes NULL et vides (lève une exception).
Tailles de morceaux inférieures à 1 (lève une exception).
Il est implémenté comme une méthode d'extension et calcule le nombre de morceaux à générer au préalable. Il vérifie le dernier morceau car dans le cas où la longueur du texte n'est pas un multiple, il doit être plus court. Propre, court, facile à comprendre ... et fonctionne!
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;}
J'aime beaucoup cette réponse, mais peut-être devriez-vous utiliser if ((i + 1) * chunk> = input.Length) au lieu de try / catch car les exceptions le sont pour des cas exceptionnels.
nelsontruran
2
Je pense que c'est une réponse simple:
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);}}
Je sais que la question date de plusieurs années, mais voici une implémentation Rx. Il gère le length % chunkSize != 0problème hors de la boîte:
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();}
J'ai un peu développé la solution de João. Ce que j'ai fait différemment, c'est que dans ma méthode, vous pouvez réellement spécifier si vous voulez retourner le tableau avec les caractères restants ou si vous voulez les tronquer si les caractères de fin ne correspondent pas à la longueur de bloc requise, je pense que c'est assez flexible et le le code est assez simple:
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;}
Je ne suis pas sûr de voir l'utilisation du back-casting Listpour IEnumerable; tout ce que cela fait est de cacher les fonctions spécifiques à la liste que vous voudrez peut-être utiliser. Il n'y a aucun inconvénient à simplement retourner le List.
Nyerguds
1
Je ne me souviens pas qui m'a donné ça, mais ça marche très bien. J'ai testé plusieurs façons de diviser les types énumérables en groupes. L'utilisation serait juste comme ça ...
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;}}
Réponses:
Veuillez noter qu'un code supplémentaire peut être requis pour gérer avec élégance les cas marginaux (
null
ou chaîne d'entrée videchunkSize == 0
,, longueur de chaîne d'entrée non divisible parchunkSize
, etc.). La question d'origine ne spécifie aucune exigence pour ces cas marginaux et dans la vie réelle, les exigences peuvent varier et sont donc hors de portée de cette réponse.la source
(i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize
. Un problème supplémentaire est que cette fonction ne tient pas compte du fait que str est nul. Cela peut être corrigé en enveloppant toute la déclaration de retour dans une autre expression ternaire:(str != null) ? ... : Enumerable.Empty<String>();
.str.Length / chunkSize
audouble length = str.Length; double size = chunkSize; int count = (int)Math.Ceiling(length/size); return Enumerable.Range(0, count)...
StringLength % 4 will always be 0
. Si ceLinq
n'est pas aussi facile à comprendre, il existe d'autres réponses qui utilisent des boucles et des rendements. Tout le monde est libre de choisir la solution qu'elle préfère. Vous pouvez poster votre code comme réponse et les gens voteront volontiers pour lui.Dans une combinaison de colombe + réponses de Konstatin ...
Cela fonctionnera pour toutes les chaînes qui peuvent être divisées en un nombre entier de morceaux et lèvera une exception dans le cas contraire.
Si vous souhaitez prendre en charge des chaînes de n'importe quelle longueur, vous pouvez utiliser le code suivant:
Cependant, le PO a déclaré explicitement qu'il n'en avait pas besoin; c'est un peu plus long et plus difficile à lire, légèrement plus lent. Dans l'esprit de KISS et YAGNI, j'irais avec la première option: c'est probablement l'implémentation la plus efficace possible, et elle est très courte, lisible et, surtout, lève une exception pour les entrées non conformes.
la source
Pourquoi pas des boucles? Voici quelque chose qui le ferait très bien:
Je ne sais pas comment vous traiteriez le cas où la chaîne n'est pas un facteur de 4, mais ne dites pas que votre idée n'est pas possible, vous vous demandez simplement la motivation si une simple boucle for le fait très bien? Évidemment, ce qui précède pourrait être nettoyé et même mis en place comme méthode d'extension.
Ou comme mentionné dans les commentaires, vous savez que c'est / 4 alors
la source
int chunkSize = 4
de la boucle. Il ne sera modifié que lors de la dernière passe.i += chunkSize
place.str.Length
la boucle de la boucle et la placer dans une variable locale. L'optimiseur C # peut être en mesure d'inline la longueur du tableau, mais je pense que le code tel qu'il est écrit fera un appel de méthode sur chaque boucle, ce qui n'est pas efficace, car la taille destr
ne change jamais.Utilisation d' expressions régulières et de Linq :
Je trouve cela plus lisible, mais ce n'est qu'une opinion personnelle. Il peut également s'agir d'un aller simple:).
la source
\d
classe de caractères par a.
et de spécifierRegexOptions.Singleline
.Ceci est basé sur la solution @dove mais implémenté comme méthode d'extension.
Avantages:
Code
Usage
Tests unitaires supprimés par souci de concision (voir la révision précédente )
la source
if (str.Length == 0) yield return String.Empty; else { for... }
IEnumerable
en tableau, surtout pas implicitement.Chunkify
.. Ce n'est pas la mienne, je ne me souviens pas où j'ai vu ce nom, mais c'était très agréable pour moiComment est-ce pour un one-liner?
Avec cette expression régulière, peu importe si le dernier morceau est inférieur à quatre caractères, car il ne regarde que les caractères derrière lui.
Je suis sûr que ce n'est pas la solution la plus efficace, mais je n'ai eu qu'à la lancer.
la source
target.Lenght % ChunckSize == 0
cela renvoie une ligne vide supplémentaire, par exempleList<string> result = new List<string>(Regex.Split("fooo", @"(?<=\G.{4})", RegexOptions.Singleline));
Ce n'est pas joli et ce n'est pas rapide, mais ça marche, c'est une doublure et c'est LINQy:
la source
ToCharArray
est inutile depuis l'string
estIEnumerable<char>
.J'ai récemment dû écrire quelque chose qui accomplit cela au travail, alors j'ai pensé publier ma solution à ce problème. En prime, la fonctionnalité de cette solution fournit un moyen de diviser la chaîne dans la direction opposée et elle gère correctement les caractères unicode comme mentionné précédemment par Marvin Pinto ci-dessus. Voici donc:
En outre, voici un lien d'image vers les résultats de l'exécution de ce code: http://i.imgur.com/16Iih.png
la source
{str.ToString()}
à la fin de votre première déclaration IF. Êtes-vous sûr de ne pas vouloir direstr.String
? J'ai eu un problème avec le code ci-dessus, j'ai fait ce changement et tout a fonctionné.Cela devrait être beaucoup plus rapide et plus efficace que l'utilisation de LINQ ou d'autres approches utilisées ici.
la source
la source
Vous pouvez utiliser morelinq par Jon Skeet. Utilisez Batch comme:
Cela renverra 4 morceaux pour la chaîne
"1111222233334444"
. Si la longueur de la chaîne est inférieure ou égale à la taille du blocBatch
, la chaîne sera renvoyée comme seul élément deIEnumerable<string>
Pour la sortie:
et cela donnera:
la source
et une autre approche:
la source
Six ans plus tard o_O
Juste parce que
ou
AFAIK tous les cas de bord sont traités.
la source
Simple et court:
la source
.
?Il gère correctement la longueur de chaîne d'entrée non divisible par chunkSize.
Veuillez noter qu'un code supplémentaire peut être requis pour gérer correctement les cas marginaux (chaîne d'entrée nulle ou vide, chunkSize == 0).
la source
Un conseil important si la chaîne qui est fragmentée doit prendre en charge tous les caractères Unicode.
Si la chaîne doit prendre en charge les caractères internationaux comme
𠀋
, divisez la chaîne à l'aide de la classe System.Globalization.StringInfo. À l'aide de StringInfo, vous pouvez fractionner la chaîne en fonction du nombre d'éléments de texte.La chaîne ci-dessus a une longueur de 2, car la
String.Length
propriété renvoie le nombre d'objets Char dans cette instance, pas le nombre de caractères Unicode.la source
Meilleure réponse, la plus simple et la plus générique :).
la source
Substring
surcharge qui ne nécessite pas le paramètre de longueuroriginalString.Substring(i)
. Vous pouvez également utiliser>
au lieu de>=
dans votre chèque.Personnellement je préfère ma solution :-)
Il gère:
Il est implémenté comme une méthode d'extension et calcule le nombre de morceaux à générer au préalable. Il vérifie le dernier morceau car dans le cas où la longueur du texte n'est pas un multiple, il doit être plus court. Propre, court, facile à comprendre ... et fonctionne!
la source
la source
Je pense que c'est une réponse simple:
Et cela couvre les cas marginaux.
la source
Je sais que la question date de plusieurs années, mais voici une implémentation Rx. Il gère le
length % chunkSize != 0
problème hors de la boîte:la source
J'ai un peu développé la solution de João. Ce que j'ai fait différemment, c'est que dans ma méthode, vous pouvez réellement spécifier si vous voulez retourner le tableau avec les caractères restants ou si vous voulez les tronquer si les caractères de fin ne correspondent pas à la longueur de bloc requise, je pense que c'est assez flexible et le le code est assez simple:
la source
la source
Modifié légèrement pour renvoyer les pièces dont la taille n'est pas égale à chunkSize
la source
List
pourIEnumerable
; tout ce que cela fait est de cacher les fonctions spécifiques à la liste que vous voudrez peut-être utiliser. Il n'y a aucun inconvénient à simplement retourner leList
.Je ne me souviens pas qui m'a donné ça, mais ça marche très bien. J'ai testé plusieurs façons de diviser les types énumérables en groupes. L'utilisation serait juste comme ça ...
Le code d'extension ressemblerait à ceci ...
la source
la source
i += offSet
dans votrefor
expression.Modifié (maintenant il accepte tout non nul
string
et tout positifchunkSize
) la solution de Konstantin Spirin :Tests:
la source
démo
la source
Basé sur d'autres réponses d'affiches, ainsi que quelques exemples d'utilisation:
la source
Utilisation des extensions Buffer de la bibliothèque IX
la source