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

199 c# string

假设我有一个字符串:

string str = "1111222233334444"; 
Run Code Online (Sandbox Code Playgroud)

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

例如,将其分解为4将返回字符串:

"1111"
"2222"
"3333"
"4444"
Run Code Online (Sandbox Code Playgroud)

Kon*_*rin 223

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

请注意,可能需要额外的代码才能正常处理边缘情况(null或空输入字符串chunkSize == 0,输入字符串长度不能被整除chunkSize等).原始问题没有规定对这些边缘情况的任何要求,并且在现实生活中,要求可能会有所不同,因此它们超出了本答案的范围.

  • 主题首发在评论中说`StringLength%4将始终为0`.如果`Linq`不那么容易理解,那么还有其他答案使用循环和产量.任何人都可以自由选择她最喜欢的解决方案.您可以将您的代码作为答案发布,人们将很乐意为其投票. (6认同)
  • 这很接近,但与之前的30个upvoters不同,我不得不将Range的循环计数限制从`str.Length/chunkSize`更改为`double length = str.Length; double size = chunkSize; int count =(int)Math.Ceiling(长度/大小); return Enumerable.Range(0,count)...` (4认同)
  • @KonstantinSpirin我同意代码是否有效.它只处理字符串是chunkSize的倍数的情况,字符串的其余部分丢失.请修改.还要记住,LINQ和它的魔力并不像那些只想查看这个问题的解决方案的人那么容易理解.一个人现在必须了解Enumerable.Range()和.Select()函数的作用.我不会争辩你应该对C#/ .NET代码有所了解,因为这些函数已经存在于BCL多年了. (4认同)
  • @Harry好抓!这可以通过substring的count参数上的drop-in三元表达式来解决.类似的东西:`(i*chunkSize + chunkSize <= str.Length)?chunkSize:str.Length - i*chunkSize`.另一个问题是此函数不考虑str为null.这可以通过将整个return语句包装在另一个三元表达式中来解决:`(str!= null)?...:Enumerable.Empty <String>();`. (3认同)
  • Enumerable.Range(0, (str.Length + chunkSize - 1) / chunkSize) .Select(i =&gt; str.Substring(i * chunkSize, Math.Min(str.Length - i * chunkSize, chunkSize))) (3认同)

Eam*_*nne 127

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

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

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

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

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));
}
Run Code Online (Sandbox Code Playgroud)

然而,在OP明确表示他并不会需要这个; 它有点长,更难阅读,稍慢.根据KISS和YAGNI的精神,我会选择第一个选项:它可能是最有效的实现,而且它非常简短,可读,并且重要的是,它会对不合格的输入抛出异常.

  • 如果你把它设为"静态... Chunk(这个字符串str,int chunkSize){"你甚至还有一个"新的"C#-Feature.然后你可以写"1111222233334444".Chunk(4). (7认同)
  • +1值得点头.有点打在头上钉.他正在寻找简洁的sytnax,你也在给予(可能)更好的表现. (4认同)

dov*_*ove 51

为什么不循环?这里有一些非常好的东西:

        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();
Run Code Online (Sandbox Code Playgroud)

我不知道你怎么处理字符串不是4的因素,但不是说你的想法是不可能的,只是想知道它的动机,如果一个简单的for循环做得很好?显然,上述内容可以清理,甚至可以作为扩展方法.

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

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


Mag*_*ron 45

从.NET 6开始,我们还可以使用以下Chunk方法:

var result = str
    .Chunk(4)
    .Select(x => new string(x))
    .ToList();
Run Code Online (Sandbox Code Playgroud)


Joã*_*lva 38

使用正则表达式Linq:

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

我发现这更具可读性,但这仅仅是个人观点.它也可以是单行:).

  • 将模式更改为@"\ d {1,4}",它适用于任何字符串长度.:) (6认同)
  • 或只是 Regex.Matches(s, @"\d{1,4}").Select(m =&gt; m.Value).ToList(); 我从来没有明白这种替代语法的意义,它只会混淆我们正在使用的扩展方法。 (4认同)
  • +1虽然这比其他解决方案慢,但它确实非常易读.我不清楚OP是否需要数字或任意字符; 用`.`替换`\ d`字符类并指定`RegexOptions.Singleline`可能是明智的. (3认同)

ole*_*sii 33

这是基于@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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

var result = "bobjoecat".SplitBy(3); // bob, joe, cat
Run Code Online (Sandbox Code Playgroud)

为简洁起见,删除了单元测试(参见上一版本)


Ala*_*ore 20

这对于一个班轮怎么样?

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

使用这个正则表达式,最后一个块是否少于四个字符并不重要,因为它只会查看它背后的字符.我确信这不是最有效的解决方案,但我只是不得不把它扔掉.:d


Guf*_*ffa 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();
Run Code Online (Sandbox Code Playgroud)


Mic*_*son 7

我最近不得不写一些在工作中完成这个的东西,所以我想我会发布我的解决方案来解决这个问题.作为一个额外的好处,这个解决方案的功能提供了一种方法,以相反的方向分割字符串,它正确处理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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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


小智 6

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

它处理:

  • 字符串长度是块大小的倍数。
  • 字符串长度不是大小的倍数。
  • 小于块大小的字符串长度。
  • 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;
}
Run Code Online (Sandbox Code Playgroud)


Jef*_*ado 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);
}
Run Code Online (Sandbox Code Playgroud)


Hab*_*bib 5

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

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

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

对于输出:

foreach (var chunk in chunks)
{
    Console.WriteLine(chunk);
}
Run Code Online (Sandbox Code Playgroud)

它会给:

1111
2222
3333
4444
Run Code Online (Sandbox Code Playgroud)

  • 在 MoreLINQ 的作者中,我看到了_Jonathan Skeet_,但没有看到_Jon Skeet_。那么你是指乔恩·斯基特,还是什么?;-) (2认同)

Ton*_*Nam 5

简单而简短:

// 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);
Run Code Online (Sandbox Code Playgroud)


Krz*_*nek 5

我知道问题已经存在多年了,但这里有一个 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();
        }
Run Code Online (Sandbox Code Playgroud)