将字符串拆分为一定大小的块


218

假设我有一个字符串:

string str = "1111222233334444"; 

如何将这个字符串分成一些大小的块?

例如,将其分成4个大小将返回字符串:

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

18
当C#的标准字符串操作函数可以更轻松,更快速地执行此操作时,为什么还要使用LINQ或正则表达式呢?另外,如果字符串长度为奇数个字符,会发生什么?
伊恩·肯普

7
“我想避免循环”-为什么?
米奇小麦,

12
使用简单的循环绝对可以带来最佳性能。
Guffa

4
nichesoftware.co.nz/blog/200909/linq-vs-loop-performance是linq和阵列上实际循环之间的相当不错的比较。我怀疑您会发现linq比手动编写的代码快得多,因为它会不断调用难以优化的运行时委托。虽然Linq更有趣:)
Blindy

2
无论您使用的是LINQ还是正则表达式,循环仍然存在。
安东·泰霍伊,09年

Answers:


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

请注意,可能需要其他代码来妥善处理边缘情况(null或输入字符串为空,chunkSize == 0不能被chunkSize等分的输入字符串长度等)。最初的问题没有为这些极端情况指定任何要求,而在现实生活中,这些要求可能会有所不同,因此超出了此答案的范围。


3
@Harry好抓住!可以使用子字符串的count参数上的三元表达式来解决这个问题。类似于:(i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize。另一个问题是该函数不能说明str为null。可以通过将整个return语句包装在另一个三元表达式中来解决此问题(str != null) ? ... : Enumerable.Empty<String>();
德鲁·斯皮克斯

7
这已经很接近了,但是与之前的30个发起者不同,我不得不将范围的循环计数限制从str.Length / chunkSize更改为double length = str.Length; double size = chunkSize; int count = (int)Math.Ceiling(length/size); return Enumerable.Range(0, count)...
间隔

4
@KonstantinSpirin我同意代码是否有效。它仅处理字符串是chunkSize的倍数的情况,其余字符串丢失的情况。请修改。还请记住,LINQ及其魔力对于只想研究此问题的解决方案的人来说并不容易理解。现在,人们必须了解Enumerable.Range()和.Select()函数的作用。我不会说您应该了解编写C#/。NET代码的知识,因为这些功能已经存在于BCL中很多年了。
CodeMonkeyKing

6
话题启动者在评论中说StringLength % 4 will always be 0。如果Linq不是那么容易理解,那么还有其他使用循环和收益的答案。任何人都可以自由选择她最喜欢的解决方案。您可以将代码发布为答案,人们会很高兴为此投票。
康斯坦丁·斯皮林

3
Enumerable.Range(0,(str.Length + chunkSize-1)/ chunkSize).Select(i => str.Substring(i * chunkSize,Math.Min(str.Length-i * chunkSize,chunkSize)))
Sten彼得罗夫

135

结合鸽子+康斯坦丁的答案...

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

这将适用于所有可以拆分为整数块的字符串,否则将引发异常。

如果要支持任何长度的字符串,可以使用以下代码:

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

然而,在OP明确表示他并不会需要这个; 它更长一些,更难阅读,有点慢。本着KISS和YAGNI的精神,我会选择第一种选择:它可能是最有效的实现方式,它非常简短,易读,而且重要的是,对于不一致的输入会抛出异常。


4
+1值得点头。有点撞在头上。他正在寻找简洁的语法,并且您还提供了(可能)更好的性能。
鸽子

7
如果您将其设置为“静态...块(此字符串str,int chunkSize){”,您甚至可以再添加一个“新” C#功能。然后,您可以编写“ 1111222233334444”。Chunk(4)。
MartinStettner

1
@MartinStettner:如果这是一个常见的操作,那肯定是一个不错的主意。
Eamon Nerbonne 09年

您应该只包括后一个代码。前者要求您在使用前了解并测试字符串是否是块大小的倍数,或者了解它不会返回字符串的其余部分。
CodeMonkeyKing

OP的问题并不清楚他是否需要该功能。第一个解决方案更简单,更快速且可靠地失败,但无法将字符串均匀地拆分为指定的块大小。我同意,返回“错误”的结果将是不好的,但不是这样做的-它只会引发异常,因此,如果可以忍受这种限制,我可以使用它。
Eamon Nerbonne

56

为什么不循环?这可以很好地做到这一点:

        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();

我不知道您如何处理字符串不是4的情况,但不是说您的想法是不可能的,只是想知道这样做的动机是否简单的for循环会很好?显然,以上内容可以清除,甚至可以作为扩展方法使用。

或者如评论中所述,您知道它是/ 4

str = "1111222233334444";
for (int i = 0; i < stringLength; i += chunkSize) 
  {Console.WriteLine(str.Substring(i, chunkSize));} 

1
您可以退出int chunkSize = 4循环。它只会在最终通过时进行修改。
约翰·费米内拉

+1是一个简单有效的解决方案-尽管我会i += chunkSize改用这种方法,但我仍会这样做。
伊恩·肯普

可能是一个小小的怪癖,但您可能还应该str.Length退出循环并进入局部变量。C#优化器可能能够内联数组长度,但是我认为编写的代码将在每个循环上进行方法调用,这是无效的,因为大小str永远不会改变。
Daniel Pryden 09年

@Daniel,把你的想法放在那里。尽管我不确定在运行时不会计算出来,但这是另一个问题;)
dove

@Daniel回到这一点,很确定编译器将提取此优化。
鸽子

41

使用正则表达式Linq

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

我觉得这更具可读性,但这只是个人观点。它也可以是单线的:)。


7
将模式更改为@“ \ d {1,4}”,它适用于任何字符串长度。:)
Guffa

3
+1尽管这比其他解决方案要慢,但绝对可读。我不清楚OP是否需要数字或任意字符;将\d字符类替换为a .并指定可能是明智的RegexOptions.Singleline
Eamon Nerbonne 2009年

2
或只是Regex.Matches(s,@“ \ d {1,4}”)。Select(m => m.Value).ToList(); 我从来没有想到过这种替代语法的含义,只是用来混淆我们在使用扩展方法。
达格2015年

38

这基于@dove解决方案,但作为扩展方法实现。

好处:

  • 扩展方式
  • 涵盖角落案例
  • 用任何字符分割字符串:数字,字母,其他符号

public static class EnumerableEx
{    
    public static IEnumerable<string> SplitBy(this string str, int chunkLength)
    {
        if (String.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.Substring(i, chunkLength);
        }
    }
}

用法

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

为了简洁起见,删除了单元测试(请参阅以前的版本


有趣的解决方案,但是为了避免在输入中超出null的检查,允许空字符串仅返回单个空字符串部分似乎更合乎逻辑:if (str.Length == 0) yield return String.Empty; else { for... }
Nyerguds

我的意思是,正常的String.Split就是这样处理空字符串。它返回一个空字符串条目。
Nyerguds

旁注:您的用法示例是错误的。您不能只转换IEnumerable为数组,尤其不能隐式转换。
Nyerguds

我个人喜欢调用该方法Chunkify..这不是我的,我不记得我在哪里看到过这个名字,但是对我来说感觉非常好
quetzalcoatl

20

一线如何?

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

使用此正则表达式,最后一块是否少于四个字符并不重要,因为它只会查看其后面的字符。

我确定这不是最有效的解决方案,但是我只好把它扔了。


在这种情况下target.Lenght % ChunckSize == 0,返回一个额外的空行,例如List<string> result = new List<string>(Regex.Split("fooo", @"(?<=\G.{4})", RegexOptions.Singleline));
fubo

9

它不是很漂亮,也不是很快,但是可以用,它是单线的,而且是LINQy:

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

是否可以保证GroupBy保留元素顺序?
康斯坦丁·斯皮林

ToCharArray因为string是,所以没有必要IEnumerable<char>
juharr

8

最近,我不得不写一些能够在工作中完成此任务的东西,因此我想我将发布针对该问题的解决方案。此外,此解决方案的功能还提供了一种以相反方向分割字符串的方法,并且可以正确处理unicode字符,如上文Marvin Pinto所述。因此,这里是:

using System;
using Extensions;

namespace TestCSharp
{
    class Program
    {
        static void Main(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
{
    public static class StringExtensions
    {
        /// <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 &gt; 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 &lt; 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>            
        public static string[] Split(this string s, int length)
        {
            System.Globalization.StringInfo str = new System.Globalization.StringInfo(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.SubstringByTextElements((iStr - lengthAbs < 0 ? 0 : iStr - lengthAbs + 1), (iStr - lengthAbs < 0 ? iStr + 1 : lengthAbs));

            return array;
        }
    }
}

另外,这是运行此代码的结果的图像链接:http : //i.imgur.com/16Iih.png


1
我注意到此代码有问题。您{str.ToString()}在第一条IF语句的末尾。你确定你不是故意的str.String吗?我上面的代码有问题,进行了更改,一切正常。
gunr2171 2012年

@ gunr2171似乎如果str == null,那么该行还将给出NullReferenceException。
John Zabroski

5

这比使用LINQ或此处使用的其他方法更快,更有效。

public static IEnumerable<string> Splice(this string 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);
}

看起来像它早检查,但事实并非如此。在开始枚举可枚举之前,您不会出错。您需要将函数分为两部分,第一部分进行参数检查,然后返回第二个私有部分(进行枚举)的结果。
ErikE

4
public static IEnumerable<IEnumerable<T>> SplitEvery<T>(this IEnumerable<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>>();
}

public static IEnumerable<T> Cons<T>(T x, IEnumerable<T> xs)
{
    yield return x;
    foreach (var xi in xs)
        yield return xi;
}

4

您可以使用Jon Skeet的morelinq。使用批处理,例如:

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

这将为字符串返回4个块"1111222233334444"。如果字符串长度小于或等于块大小,Batch则将字符串作为唯一的元素返回IEnumerable<string>

对于输出:

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

它会给:

1111
2222
3333
4444

在MoreLINQ的作者中,我看到乔纳森斯基(Jonathan Skeet),但没有乔恩斯基(Jon Skeet)。所以你的意思乔恩斯基特,还是什么?;-)
Sнаđошƒаӽ

3
static IEnumerable<string> Split(string str, double chunkSize)
{
    return Enumerable.Range(0, (int) Math.Ceiling(str.Length/chunkSize))
       .Select(i => new string(str
           .Skip(i * (int)chunkSize)
           .Take((int)chunkSize)
           .ToArray()));
}

另一种方法:

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.ChunkString(2)) Console.WriteLine(i);
    }
}

public static class Ext{
    public static IEnumerable<string> ChunkString(this string 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));
    }
}

3

六年后o_O

只是因为

    public static IEnumerable<string> Split(this string str, int chunkSize, bool remainingInFront)
    {
        var count = (int) Math.Ceiling(str.Length/(double) chunkSize);
        Func<int, int> start = index => remainingInFront ? str.Length - (count - index)*chunkSize : index*chunkSize;
        Func<int, int> 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.Substring(Math.Max(start(i), 0),end(i)));
    }

要么

    private static Func<bool, int, int, int, int, int> start = (remainingInFront, length, count, index, size) =>
        remainingInFront ? length - (count - index) * size : index * size;

    private static Func<bool, int, int, int, int, int, int> 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<string> Split(this string str, int chunkSize, bool remainingInFront)
    {
        var count = (int)Math.Ceiling(str.Length / (double)chunkSize);
        return Enumerable.Range(0, count).Select(i => str.Substring(
            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处理所有边缘情况。

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

“输入为空字符串”边缘字符呢?我希望像Split一样,返回一个IEnumerable,其中包含一个包含输入项的空字符串。
Nyerguds

3

简单而简短:

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

为什么不使用.
marsze

3
static IEnumerable<string> Split(string str, int chunkSize)
{
   IEnumerable<string> retVal = Enumerable.Range(0, str.Length / chunkSize)
        .Select(i => str.Substring(i * chunkSize, chunkSize))

   if (str.Length % chunkSize > 0)
        retVal = retVal.Append(str.Substring(str.Length / chunkSize * chunkSize, str.Length % chunkSize));

   return retVal;
}

它可以正确处理不能被chunkSize整除的输入字符串长度。

请注意,可能需要其他代码来妥善处理边缘情况(空或空输入字符串,chunkSize == 0)。


2

重要提示,如果要分块的字符串需要支持所有Unicode字符。

如果字符串支持国际字符,例如𠀋,则使用System.Globalization.StringInfo类拆分字符串。使用StringInfo,您可以根据文本元素的数量拆分字符串。

string internationalString = '𠀋';

上面的字符串的长度为2,因为该String.Length属性在此实例中返回Char对象的数量,而不是Unicode字符的数量。


2

最佳,最简单和通用的答案:)。

    string originalString = "1111222233334444";
    List<string> test = new List<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))));
    }

在最后一行计算长度是多余的,只需使用Substring不需要length参数的重载即可originalString.Substring(i)。您也可以使用>而不是>=在支票中。
拉西尔·希兰

@RacilHilan我将根据您的建议测试代码更改并更新答案。我很高兴享有如此良好声誉的人有时间审查我的代码。:)谢谢,Sandeep
Sandeep Kushwah 18'Aug

2

我个人更喜欢我的解决方案:-)

它处理:

  • 字符串长度是块大小的倍数。
  • 字符串长度不是块大小的倍数。
  • 小于块大小的字符串长度。
  • NULL和空字符串(引发异常)。
  • 块大小小于1(引发异常)。

它是作为扩展方法实现的,它可以计算要预先生成的块数。它检查最后一个块,因为如果文本长度不是整数倍,则需要将其缩短。干净,简短,易于理解...并且可以工作!

    public static string[] Split(this string value, int chunkSize)
    {
        if (string.IsNullOrEmpty(value)) throw new ArgumentException("The string 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 string[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;
    }

2
List<string> SplitString(int chunk, string input)
{
    List<string> list = new List<string>();
    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;
}

1
我非常喜欢这个答案,但也许您应该使用if((i + 1)* chunk> = input.Length)而不是try / catch,因为例外情况是特殊情况。
nelsontruran '18 -4-2

2

我认为这是一个简单的答案:

public static IEnumerable<string> Split(this string str, int chunkSize)
    {
        if(string.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.Substring(startIndex);
            else
                yield return str.Substring(startIndex, chunkSize);
        }
    }

它涵盖了边缘情况。


2

我知道问题已经存在多年了,但这是一个Rx实现。它length % chunkSize != 0开箱即用地解决了这个问题:

   public static IEnumerable<string> Chunkify(this string input, int size)
        {
            if(size < 1)
                throw new ArgumentException("size must be greater than 0");

            return input.ToCharArray()
                .ToObservable()
                .Buffer(size)            
                .Select(x => new string(x.ToArray()))
                .ToEnumerable();
        }

1

我在João的解决方案上略有建树。我做的不同的是,在我的方法中,您实际上可以指定是要返回包含剩余字符的数组,还是要在结尾字符与所需的块长度不匹配时截断它们,我想这很灵活,并且代码相当简单:

using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace SplitFunction
{
    class Program
    {
        static void Main(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();
        }

        private static string[] 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 = (from Match m in Regex.Matches(text,pattern)select m.Value).ToArray(); 

            return chunks;
        }     
    }
}

1
    public static List<string> SplitByMaxLength(this string str)
    {
        List<string> splitString = new List<string>();

        for (int index = 0; index < str.Length; index += MaxLength)
        {
            splitString.Add(str.Substring(index, Math.Min(MaxLength, str.Length - index)));
        }

        return splitString;
    }

您,忘记了MaxLength参数。
Nyerguds

1

稍作更改以返回大小不等于chunkSize的部件

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

不确定我看到使用反向广播ListIEnumerable;所有这些都隐藏了您可能想使用的特定于列表的功能。退货没有任何弊端List
Nyerguds

1

我不记得是谁给我的,但是效果很好。我快速测试了将Enumerable类型分为几类的多种方法。用法就是这样...

List<string> Divided = Source3.Chunk(24).Select(Piece => string.Concat<char>(Piece)).ToList();

扩展代码看起来像这样...

#region Chunk Logic
private class ChunkedEnumerable<T> : IEnumerable<T>
{
    class ChildEnumerator : IEnumerator<T>
    {
        ChunkedEnumerable<T> parent;
        int position;
        bool done = false;
        T current;


        public ChildEnumerator(ChunkedEnumerable<T> 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<T> wrapper;
    int chunkSize;
    int start;

    public ChunkedEnumerable(EnumeratorWrapper<T> wrapper, int chunkSize, int start)
    {
        this.wrapper = wrapper;
        this.chunkSize = chunkSize;
        this.start = start;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new ChildEnumerator(this);
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

}
private class EnumeratorWrapper<T>
{
    public EnumeratorWrapper(IEnumerable<T> source)
    {
        SourceEumerable = source;
    }
    IEnumerable<T> SourceEumerable { get; set; }

    Enumeration currentEnumeration;

    class Enumeration
    {
        public IEnumerator<T> 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();
        }
    }
}
/// <summary>Speed Checked.  Works Great!</summary>
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, int chunksize)
{
    if (chunksize < 1) throw new InvalidOperationException();

    var wrapper = new EnumeratorWrapper<T>(source);

    int currentPos = 0;
    T ignore;
    try
    {
        wrapper.AddRef();
        while (wrapper.Get(currentPos, out ignore))
        {
            yield return new ChunkedEnumerable<T>(wrapper, chunksize, currentPos);
            currentPos += chunksize;
        }
    }
    finally
    {
        wrapper.RemoveRef();
    }
}
#endregion

1
class StringHelper
{
    static void Main(string[] args)
    {
        string str = "Hi my name is vikas bansal and my email id is bansal.vks@gmail.com";
        int offSet = 10;

        List<string> chunks = chunkMyStr(str, offSet);

        Console.Read();
    }

    static List<string> chunkMyStr(string str, int offSet)
    {


        List<string> resultChunks = new List<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;
    }
}

您可以略微改善您的代码:将增量表达式i += offSet转换为for表达式。
JimiLoe 2015年

1

改性(现在它接受任何非空string任何阳性chunkSize康斯坦丁施普瑞的溶液:

public static IEnumerable<String> Split(String 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));
}

测试:

  String source = @"ABCDEF";

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

1
static List<string> GetChunks(string value, int chunkLength)
{
    var res = new List<string>();
    int count = (value.Length / chunkLength) + (value.Length % chunkLength > 0 ? 1 : 0);
    Enumerable.Range(0, count).ToList().ForEach(f => res.Add(value.Skip(f * chunkLength).Take(chunkLength).Select(z => z.ToString()).Aggregate((a,b) => a+b)));
    return res;
}

演示


这个保留了字符串的其余部分(后拆分),甚至比“ chunkLenght”还短,谢谢
Jason Loki Smith

0

基于其他张贴者的答案以及一些使用示例:

public static string FormatSortCode(string sortCode)
{
    return ChunkString(sortCode, 2, "-");
}
public static string FormatIBAN(string iban)
{
    return ChunkString(iban, 4, "&nbsp;&nbsp;");
}

private static string ChunkString(string str, int chunkSize, string separator)
{
    var b = new StringBuilder();
    var stringLength = str.Length;
    for (var i = 0; i < stringLength; i += chunkSize)
    {
        if (i + chunkSize > stringLength) chunkSize = stringLength - i;
        b.Append(str.Substring(i, chunkSize));
        if (i+chunkSize != stringLength)
            b.Append(separator);
    }
    return b.ToString();
}

0

使用IX库中的Buffer扩展

    static IEnumerable<string> Split( this string str, int chunkSize )
    {
        return str.Buffer(chunkSize).Select(l => String.Concat(l));
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.