Fractionner une chaîne en morceaux d’une certaine taille

Supposons que j’ai une chaîne:

ssortingng str = "1111222233334444"; 

Comment puis-je diviser cette chaîne en morceaux d’une certaine taille?

par exemple, diviser cette taille en 4 renverrait des chaînes:

 "1111" "2222" "3333" "4444" 

 static IEnumerable Split(ssortingng str, int chunkSize) { return Enumerable.Range(0, str.Length / chunkSize) .Select(i => str.Subssortingng(i * chunkSize, chunkSize)); } 

Veuillez noter qu’un code supplémentaire peut être requirejs pour gérer chunkSize == 0 cas d’arête (chaîne d’entrée vide ou vide, chunkSize == 0 , longueur de chaîne en entrée non divisible par chunkSize , etc.). La question initiale ne spécifie aucune exigence pour ces cas limites et, dans la réalité, les exigences peuvent varier et ne sont donc pas couvertes par cette réponse.

Dans une combinaison des réponses de colombe + Konstatin …

 static IEnumerable WholeChunks(ssortingng str, int chunkSize) { for (int i = 0; i < str.Length; i += chunkSize) yield return str.Substring(i, chunkSize); } 

Cela fonctionnera pour toutes les chaînes qui peuvent être divisées en un nombre entier de morceaux, et jettera une exception sinon.

Si vous souhaitez prendre en charge des chaînes de longueur quelconque, vous pouvez utiliser le code suivant:

 static IEnumerable ChunksUpto(ssortingng str, int maxChunkSize) { for (int i = 0; i < str.Length; i += maxChunkSize) yield return str.Substring(i, Math.Min(maxChunkSize, str.Length-i)); } 

Cependant, l'OP a explicitement déclaré qu'il n'en avait pas besoin; c'est un peu plus long et 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, lance une exception pour les entrées non conformes.

Pourquoi pas des boucles? Voici quelque chose qui le ferait très bien:

  ssortingng str = "111122223333444455"; int chunkSize = 4; int ssortingngLength = str.Length; for (int i = 0; i < stringLength ; i += chunkSize) { if (i + chunkSize > ssortingngLength) chunkSize = ssortingngLength - i; Console.WriteLine(str.Subssortingng(i, chunkSize)); } Console.ReadLine(); 

Je ne sais pas comment vous géreriez le cas où la chaîne n’est pas un facteur de 4, mais ne pas dire que vous êtes une idée n’est pas possible, vous demandez-vous la motivation si une boucle simple le fait très bien? De toute évidence, ce qui précède pourrait être nettoyé et même mis en œuvre 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));} 

Utiliser des expressions régulières et Linq :

 List groups = (from Match m in Regex.Matches(str, @"\d{4}") select m.Value).ToList(); 

Je trouve cela plus lisible, mais c’est juste une opinion personnelle. Il peut aussi s’agir d’un one-liner:).

Ceci est basé sur la solution @dove mais implémenté en tant que méthode d’extension.

Avantages:

  • Méthode d’extension
  • Couvre les valises d’angle
  • Division d’une chaîne avec des caractères quelconques: chiffres, lettres, autres symboles

Code

 public static class EnumerableEx { public static IEnumerable SplitBy(this ssortingng str, int chunkLength) { if (Ssortingng.IsNullOrEmpty(str)) throw new ArgumentException(); if (chunkLength < 1) throw new ArgumentException(); for (int i = 0; i < str.Length; i += chunkLength) { if (chunkLength + i > str.Length) chunkLength = str.Length - i; yield return str.Subssortingng(i, chunkLength); } } } 

Usage

 var result = "bobjoecat".SplitBy(3); // bob, joe, cat 

Tests unitaires supprimés pour des raisons de concision (voir révision précédente )

Comment ça se passe pour un one-liner?

 List result = new List(Regex.Split(target, @"(?<=\G.{4})", RegexOptions.Singleline)); 

Avec cette regex, peu importe que le dernier morceau comporte moins de 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 devais simplement la lancer. :RÉ

Ce n’est pas joli et ce n’est pas rapide, mais ça marche, c’est un one-liner et c’est LINQy:

 List a = text.Select((c, i) => new { Char = c, Index = i }).GroupBy(o => o.Index / 4).Select(g => new Ssortingng(g.Select(o => o.Char).ToArray())).ToList(); 

J’ai récemment dû écrire quelque chose qui accomplit ceci au travail, alors j’ai pensé que je posterais ma solution à ce problème. En prime, la fonctionnalité de cette solution permet de diviser la chaîne dans la direction opposée et gère correctement les caractères Unicode, comme mentionné précédemment par Marvin Pinto. Donc, la voici:

 using System; using Extensions; namespace TestCSharp { class Program { static void Main(ssortingng[] args) { ssortingng asciiStr = "This is a ssortingng."; ssortingng unicodeStr = "これは文字列です。"; ssortingng[] array1 = asciiStr.Split(4); ssortingng[] array2 = asciiStr.Split(-4); ssortingng[] array3 = asciiStr.Split(7); ssortingng[] array4 = asciiStr.Split(-7); ssortingng[] array5 = unicodeStr.Split(5); ssortingng[] array6 = unicodeStr.Split(-5); } } } namespace Extensions { public static class SsortingngExtensions { /// Returns a ssortingng array that contains the subssortingngs in this ssortingng that are seperated a given fixed length. /// This ssortingng object. /// Size of each subssortingng. /// CASE: length > 0 , RESULT: Ssortingng is split from left to right. /// CASE: length == 0 , RESULT: Ssortingng is returned as the only entry in the array. /// CASE: length < 0 , RESULT: Ssortingng is split from right to left. ///  /// Ssortingng array that has been split into subssortingngs of equal length. ///  ///  /// ssortingng s = "1234567890"; /// ssortingng[] a = s.Split(4); // a == { "1234", "5678", "90" } ///  ///  public static ssortingng[] Split(this ssortingng s, int length) { System.Globalization.SsortingngInfo str = new System.Globalization.SsortingngInfo(s); int lengthAbs = Math.Abs(length); if (str == null || str.LengthInTextElements == 0 || lengthAbs == 0 || str.LengthInTextElements <= lengthAbs) return new string[] { str.ToString() }; string[] array = new string[(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.SubssortingngByTextElements((iStr - lengthAbs < 0 ? 0 : iStr - lengthAbs + 1), (iStr - lengthAbs < 0 ? iStr + 1 : lengthAbs)); return array; } } } 

En outre, voici un lien vers l'image des résultats de l'exécution de ce code: http://soffr.miximages.com/string/16Iih.png

Cela devrait être beaucoup plus rapide et efficace que d’utiliser LINQ ou d’autres approches utilisées ici.

 public static IEnumerable Splice(this ssortingng s, int spliceLength) { if (s == null) throw new ArgumentNullException("s"); if (spliceLength < 1) throw new ArgumentOutOfRangeException("spliceLength"); if (s.Length == 0) yield break; var start = 0; for (var end = spliceLength; end < s.Length; end += spliceLength) { yield return s.Substring(start, spliceLength); start = end; } yield return s.Substring(start); } 
 public static IEnumerable> SplitEvery(this IEnumerable values, int n) { var ls = values.Take(n); var rs = values.Skip(n); return ls.Any() ? Cons(ls, SplitEvery(rs, n)) : Enumerable.Empty>(); } public static IEnumerable Cons(T x, IEnumerable xs) { yield return x; foreach (var xi in xs) yield return xi; } 

Vous pouvez utiliser morelinq de Jon Skeet. Utilisez le lot comme:

 ssortingng str = "1111222233334444"; int chunkSize = 4; var chunks = str.Batch(chunkSize).Select(r => new Ssortingng(r.ToArray())); 

Cela retournera 4 morceaux pour la chaîne "1111222233334444" . Si la longueur de la chaîne est inférieure ou égale à la taille du bloc, Batch renvoie la chaîne en tant que seul élément de IEnumerable

Pour sortie:

 foreach (var chunk in chunks) { Console.WriteLine(chunk); } 

et ça donnera:

 1111 2222 3333 4444 

Six ans plus tard o_O

Juste parce que

  public static IEnumerable Split(this ssortingng str, int chunkSize, bool remainingInFront) { var count = (int) Math.Ceiling(str.Length/(double) chunkSize); Func start = index => remainingInFront ? str.Length - (count - index)*chunkSize : index*chunkSize; Func end = index => Math.Min(str.Length - Math.Max(start(index), 0), Math.Min(start(index) + chunkSize - Math.Max(start(index), 0), chunkSize)); return Enumerable.Range(0, count).Select(i => str.Subssortingng(Math.Max(start(i), 0),end(i))); } 

ou

  private static Func start = (remainingInFront, length, count, index, size) => remainingInFront ? length - (count - index) * size : index * size; private static Func end = (remainingInFront, length, count, index, size, start) => Math.Min(length - Math.Max(start, 0), Math.Min(start + size - Math.Max(start, 0), size)); public static IEnumerable Split(this ssortingng str, int chunkSize, bool remainingInFront) { var count = (int)Math.Ceiling(str.Length / (double)chunkSize); return Enumerable.Range(0, count).Select(i => str.Subssortingng( Math.Max(start(remainingInFront, str.Length, count, i, chunkSize), 0), end(remainingInFront, str.Length, count, i, chunkSize, start(remainingInFront, str.Length, count, i, chunkSize)) )); } 

AFAIK tous les cas de bord sont traités.

 Console.WriteLine(ssortingng.Join(" ", "abc".Split(2, false))); // ab c Console.WriteLine(ssortingng.Join(" ", "abc".Split(2, true))); // a bc Console.WriteLine(ssortingng.Join(" ", "a".Split(2, true))); // a Console.WriteLine(ssortingng.Join(" ", "a".Split(2, false))); // a 

Un conseil important si la chaîne en cours de création doit prendre en charge tous les caractères Unicode.

Si la chaîne doit prendre en charge des caractères internationaux tels que 𠀋 , 𠀋 la chaîne à l’aide de la classe System.Globalization.SsortingngInfo. En utilisant SsortingngInfo, vous pouvez diviser la chaîne en fonction du nombre d’éléments de texte.

 ssortingng internationalSsortingng = '𠀋'; 

La chaîne ci-dessus a une longueur de 2, car la propriété Ssortingng.Length renvoie le nombre d’objects Char dans cette instance, pas le nombre de caractères Unicode.

Meilleure, la plus simple et la réponse générique :).

  ssortingng originalSsortingng = "1111222233334444"; List test = new List(); int chunkSize = 4; // change 4 with the size of ssortingngs you want. for (int i = 0; i < originalString.Length; i = i + chunkSize) { if (originalString.Length - i >= chunkSize) test.Add(originalSsortingng.Subssortingng(i, chunkSize)); else test.Add(originalSsortingng.Subssortingng(i,((originalSsortingng.Length - i)))); } 
 static IEnumerable Split(ssortingng str, double chunkSize) { return Enumerable.Range(0, (int) Math.Ceiling(str.Length/chunkSize)) .Select(i => new ssortingng(str .Skip(i * (int)chunkSize) .Take((int)chunkSize) .ToArray())); } 

et une autre approche:

 using System; using System.Collections.Generic; using System.Linq; public class Program { public static void Main() { var x = "Hello World"; foreach(var i in x.ChunkSsortingng(2)) Console.WriteLine(i); } } public static class Ext{ public static IEnumerable ChunkSsortingng(this ssortingng val, int chunkSize){ return val.Select((x,i) => new {Index = i, Value = x}) .GroupBy(x => x.Index/chunkSize, x => x.Value) .Select(x => ssortingng.Join("",x)); } } 

Personnellement je préfère ma solution 🙂

Il gère:

  • Les 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 du bloc.
  • Des longueurs de chaîne plus petites que la taille du bloc.
  • NULL et des chaînes vides (lève une exception).
  • Taille des blocs inférieure à 1 (lève une exception).

Il est implémenté en tant que méthode d’extension et calcule le nombre de blocs à générer au préalable. Il vérifie le dernier segment car si la longueur du texte n’est pas un multiple, il doit être plus court. Propre, bref, facile à comprendre… et fonctionne!

  public static ssortingng[] Split(this ssortingng value, int chunkSize) { if (ssortingng.IsNullOrEmpty(value)) throw new ArgumentException("The ssortingng cannot be null."); if (chunkSize < 1) throw new ArgumentException("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 = new ssortingng[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.Subssortingng(i * chunkSize, lastChunkSize); return result; } 
 List SplitSsortingng(int chunk, ssortingng input) { List list = new List(); int cycles = input.Length / chunk; if (input.Length % chunk != 0) cycles++; for (int i = 0; i < cycles; i++) { try { list.Add(input.Substring(i * chunk, chunk)); } catch { list.Add(input.Substring(i * chunk)); } } return list; } 

J’ai légèrement développé la solution de João. Ce que j’ai fait différemment, c’est que dans ma méthode, vous pouvez spécifier si vous voulez renvoyer le tableau avec les caractères restants ou si vous voulez les tronquer si les caractères de fin ne correspondent pas à votre longueur de morceau requirejse. le code est assez simple:

 using System; using System.Linq; using System.Text.RegularExpressions; namespace SplitFunction { class Program { static void Main(ssortingng[] args) { ssortingng text = "hello, how are you doing today?"; ssortingng[] chunks = SplitIntoChunks(text, 3,false); if (chunks != null) { chunks.ToList().ForEach(e => Console.WriteLine(e)); } Console.ReadKey(); } private static ssortingng[] SplitIntoChunks(ssortingng text, int chunkSize, bool truncateRemaining) { ssortingng chunk = chunkSize.ToSsortingng(); ssortingng pattern = truncateRemaining ? ".{" + chunk + "}" : ".{1," + chunk + "}"; ssortingng[] chunks = null; if (chunkSize > 0 && !Ssortingng.IsNullOrEmpty(text)) chunks = (from Match m in Regex.Matches(text,pattern)select m.Value).ToArray(); return chunks; } } } 
  public static List SplitByMaxLength(this ssortingng str) { List splitSsortingng = new List(); for (int index = 0; index < str.Length; index += MaxLength) { splitString.Add(str.Substring(index, Math.Min(MaxLength, str.Length - index))); } return splitString; } 

Légèrement modifié pour renvoyer des parties dont la taille n’est pas égale à chunkSize

 public static IEnumerable Split(this ssortingng str, int chunkSize) { var splits = new List(); if (str.Length < chunkSize) { chunkSize = str.Length; } splits.AddRange(Enumerable.Range(0, str.Length / chunkSize).Select(i => str.Subssortingng(i * chunkSize, chunkSize))); splits.Add(str.Length % chunkSize > 0 ? str.Subssortingng((str.Length / chunkSize) * chunkSize, str.Length - ((str.Length / chunkSize) * chunkSize)) : ssortingng.Empty); return (IEnumerable)splits; } 

Je ne me souviens plus qui m’a donné ça, mais ça marche très bien. J’ai rapidement testé un certain nombre de façons de diviser les types Enumerable en groupes. L’utilisation serait juste comme ça …

 List Divided = Source3.Chunk(24).Select(Piece => ssortingng.Concat(Piece)).ToList(); 

Le code d’extension ressemblerait à ceci …

 #region Chunk Logic private class ChunkedEnumerable : IEnumerable { class ChildEnumerator : IEnumerator { ChunkedEnumerable parent; int position; bool done = false; T current; public ChildEnumerator(ChunkedEnumerable parent) { this.parent = parent; position = -1; parent.wrapper.AddRef(); } public T Current { get { if (position == -1 || done) { throw new InvalidOperationException(); } return current; } } public void Dispose() { if (!done) { done = true; parent.wrapper.RemoveRef(); } } object System.Collections.IEnumerator.Current { get { return Current; } } public bool MoveNext() { position++; if (position + 1 > parent.chunkSize) { done = true; } if (!done) { done = !parent.wrapper.Get(position + parent.start, out current); } return !done; } public void Reset() { // per http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx throw new NotSupportedException(); } } EnumeratorWrapper wrapper; int chunkSize; int start; public ChunkedEnumerable(EnumeratorWrapper wrapper, int chunkSize, int start) { this.wrapper = wrapper; this.chunkSize = chunkSize; this.start = start; } public IEnumerator GetEnumerator() { return new ChildEnumerator(this); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } private class EnumeratorWrapper { public EnumeratorWrapper(IEnumerable source) { SourceEumerable = source; } IEnumerable SourceEumerable { get; set; } Enumeration currentEnumeration; class Enumeration { public IEnumerator Source { get; set; } public int Position { get; set; } public bool AtEnd { get; set; } } public bool Get(int pos, out T item) { if (currentEnumeration != null && currentEnumeration.Position > pos) { currentEnumeration.Source.Dispose(); currentEnumeration = null; } if (currentEnumeration == null) { currentEnumeration = new Enumeration { Position = -1, Source = SourceEumerable.GetEnumerator(), AtEnd = false }; } item = default(T); if (currentEnumeration.AtEnd) { return false; } while (currentEnumeration.Position < pos) { currentEnumeration.AtEnd = !currentEnumeration.Source.MoveNext(); currentEnumeration.Position++; if (currentEnumeration.AtEnd) { return false; } } item = currentEnumeration.Source.Current; return true; } int refs = 0; // needed for dispose semantics public void AddRef() { refs++; } public void RemoveRef() { refs--; if (refs == 0 && currentEnumeration != null) { var copy = currentEnumeration; currentEnumeration = null; copy.Source.Dispose(); } } } /// Speed Checked. Works Great! public static IEnumerable> Chunk(this IEnumerable source, int chunksize) { if (chunksize < 1) throw new InvalidOperationException(); var wrapper = new EnumeratorWrapper(source); int currentPos = 0; T ignore; try { wrapper.AddRef(); while (wrapper.Get(currentPos, out ignore)) { yield return new ChunkedEnumerable(wrapper, chunksize, currentPos); currentPos += chunksize; } } finally { wrapper.RemoveRef(); } } #endregion 
 class SsortingngHelper { static void Main(ssortingng[] args) { ssortingng str = "Hi my name is vikas bansal and my email id is [email protected]"; int offSet = 10; List chunks = chunkMyStr(str, offSet); Console.Read(); } static List chunkMyStr(ssortingng str, int offSet) { List resultChunks = new List(); 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; } } 

Modifié (maintenant, il accepte toute ssortingng non nulle et toute chunkSize de chunkSize positive) La solution de Konstantin Spirin :

 public static IEnumerable Split(Ssortingng value, int chunkSize) { if (null == value) throw new ArgumentNullException("value"); else if (chunkSize <= 0) throw new ArgumentOutOfRangeException("chunkSize", "Chunk size should be positive"); return Enumerable .Range(0, value.Length / chunkSize + ((value.Length % chunkSize) == 0 ? 0 : 1)) .Select(index => (index + 1) * chunkSize < value.Length ? value.Substring(index * chunkSize, chunkSize) : value.Substring(index * chunkSize)); } 

Tests:

  Ssortingng source = @"ABCDEF"; // "ABCD,EF" Ssortingng test1 = Ssortingng.Join(",", Split(source, 4)); // "AB,CD,EF" Ssortingng test2 = Ssortingng.Join(",", Split(source, 2)); // "ABCDEF" Ssortingng test3 = Ssortingng.Join(",", Split(source, 123)); 

Simple et court:

 // this means match a space or not a space (anything) up to 4 characters var lines = Regex.Matches(str, @"[\s\S]{0,4}").Cast().Select(x => x.Value); 

Sur la base d’autres réponses aux affiches, accompagnées de quelques exemples d’usage:

 public static ssortingng FormatSortCode(ssortingng sortCode) { return ChunkSsortingng(sortCode, 2, "-"); } public static ssortingng FormatIBAN(ssortingng iban) { return ChunkSsortingng(iban, 4, "  "); } private static ssortingng ChunkSsortingng(ssortingng str, int chunkSize, ssortingng separator) { var b = new SsortingngBuilder(); var ssortingngLength = str.Length; for (var i = 0; i < stringLength; i += chunkSize) { if (i + chunkSize > ssortingngLength) chunkSize = ssortingngLength - i; b.Append(str.Subssortingng(i, chunkSize)); if (i+chunkSize != ssortingngLength) b.Append(separator); } return b.ToSsortingng(); } 

Utilisation des extensions de tampon de la bibliothèque IX

  static IEnumerable Split( this ssortingng str, int chunkSize ) { return str.Buffer(chunkSize).Select(l => Ssortingng.Concat(l)); } 

Cela peut être fait de cette manière aussi

  ssortingng actualSsortingng = "1111222233334444"; var listResult = new List(); int groupingLength = actualSsortingng.Length % 4; if (groupingLength > 0) listResult.Add(actualSsortingng.Subssortingng(0, groupingLength)); for (int i = groupingLength; i < actualString.Length; i += 4) { listResult.Add(actualString.Substring(i, 4)); } foreach(var res in listResult) { Console.WriteLine(res); } Console.Read(); 

If necessary to split by few different length: For example you have date and time in specify format ssortingng strangeStr = "07092016090532"; 07092016090532 (Date:07.09.2016 Time: 09:05:32)

 public static IEnumerable SplitBy(this ssortingng str, int[] chunkLength) { if (Ssortingng.IsNullOrEmpty(str)) throw new ArgumentException(); int i = 0; for (int j = 0; j < chunkLength.Length; j++) { if (chunkLength[j] < 1) throw new ArgumentException(); if (chunkLength[j] + i > str.Length) { chunkLength[j] = str.Length - i; } yield return str.Subssortingng(i, chunkLength[j]); i += chunkLength[j]; } } 

en utilisant:

 ssortingng[] dt = strangeStr.SplitBy(new int[] { 2, 2, 4, 2, 2, 2, 2 }).ToArray(); 

I think this is an straight forward answer:

 public static IEnumerable Split(this ssortingng str, int chunkSize) { if(ssortingng.IsNullOrEmpty(str) || chunkSize<1) throw new ArgumentException("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) yield return str.Subssortingng(startIndex); else yield return str.Subssortingng(startIndex, chunkSize); } } 

And it covers edge cases.

 static IEnumerable Split(ssortingng str, int chunkSize) { IEnumerable retVal = Enumerable.Range(0, str.Length / chunkSize) .Select(i => str.Subssortingng(i * chunkSize, chunkSize)) if (str.Length % chunkSize > 0) retVal = retVal.Append(str.Subssortingng(str.Length / chunkSize * chunkSize, str.Length % chunkSize)); return retVal; } 

It correctly handles input ssortingng length not divisible by chunkSize.

Please note that additional code might be required to gracefully handle edge cases (null or empty input ssortingng, chunkSize == 0).