将字符串分割为512个字符的块

10

也许是一个基本问题,但假设我有一个长度为2000个字符的字符串,我需要将这个字符串分成最多512个字符的块。

是否有一种不错的方法,比如循环之类的,可以做到这一点?


你确定需要512个字符的块吗?因为这与512字节不同,而512字节是更常见的限制。 - H H
1
@Henk:另一方面,基于字节文本分成块会相当奇怪——结果将取决于编码。 - Jon Skeet
Jon,是的,当重新组装文本时常见的问题。但是有些I/O通道以512字节块操作。 - H H
Abel,我知道,Jon也知道。我是在问meep确认条件适用的级别。512比字符更接近一个圆整的字节数。 - H H
可能是将字符串分割成特定大小的块的重复问题。 - Chris
显示剩余2条评论
8个回答

21

类似这样:

private IList<string> SplitIntoChunks(string text, int chunkSize)
{
    List<string> chunks = new List<string>();
    int offset = 0;
    while (offset < text.Length)
    {
        int size = Math.Min(chunkSize, text.Length - offset);
        chunks.Add(text.Substring(offset, size));
        offset += size;
    }
    return chunks;
}

或者只是迭代:

private IEnumerable<string> SplitIntoChunks(string text, int chunkSize)
{
    int offset = 0;
    while (offset < text.Length)
    {
        int size = Math.Min(chunkSize, text.Length - offset);
        yield return text.Substring(offset, size);
        offset += size;
    }
}

请注意,这样拆分会将文本分成UTF-16代码单元的块,这与将文本分成Unicode代码点的块并不完全相同,而后者又可能与将文本分成字形块的方式也不相同。


这个算法(以及它与Unicode的兼容性)也在Code Review上进行了讨论:将字符串分成相同长度的块 - Adriano Repetti
@AdrianoRepetti:谢谢 - 我也在答案上添加了一个小注释。 - Jon Skeet

3
尽管这个问题已经有一个被接受的答案,但是通过正则表达式提供一个简短的版本也是可以的。纯粹主义者可能不喜欢它(可以理解),但当你需要一个快速解决方案并且你擅长使用正则表达式时,这可能就是它了。性能相当不错,令人惊讶。
string [] split = Regex.Split(yourString, @"(?<=\G.{512})");

它是做什么的?使用负回溯和\G来记住最后一个位置。即使无法被512整除,它也会捕获最后一位。


在即时窗口中检查长字符串非常有用。 - Dialecticus
1
为什么不呢?它很简短,足够快速和清晰(如果你懂正则表达式的话,个人认为我需要读三遍并测试一下)。很好的版本! - Adriano Repetti

3
使用Jon的实现和yield关键字。
IEnumerable<string> Chunks(string text, int chunkSize)
{
    for (int offset = 0; offset < text.Length; offset += chunkSize)
    {
        int size = Math.Min(chunkSize, text.Length - offset);
        yield return text.Substring(offset, size);
    }
}

有趣的使用for循环与我的while循环……我正在尝试决定哪个更容易阅读。顺便说一句,你不需要在最后加上yield break。 - Jon Skeet
我擅自修复了冗余的 yield break,就像 @Jon 提到的那样。 - Louis Rhys

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

来源:将字符串分割成特定大小的块


9
在这种情况下未能提供最终块。 - Jon Skeet

1

我敢提供一个更加LINQ化的版本,基于这样一个事实: string 类型实现了 IEnumerable<char> 接口:

private IList<string> SplitIntoChunks(string text, int chunkSize)
{
    var chunks = new List<string>();
    int offset = 0;
    while(offset < text.Length) {
        chunks.Add(new string(text.Skip(offset).Take(chunkSize).ToArray()));
        offset += chunkSize;
    }
    return chunks;
}

1
我确实考虑过这个问题 - 特别是因为MoreLINQ提供了一个很好的Partition方法来处理这种情况。然而,这样做的效率会非常糟糕 :( - Jon Skeet
顺便提一下,String 没有“Skip”扩展方法,你需要先使用 ToCharArray。 - Stan R.
我知道它实现了*IEnumerable<char>*,这让它更加令人困惑... - Stan R.
@Stan:C# VS团队硬编码了字符串下拉帮助程序:这是一个特殊情况,您看不到框架提供的扩展方法。他们认为这更清晰。相反,VB团队决定在下拉助手中显示IEnumerable<char>扩展方法。 - Abel
啊,伙计们,这就解释了,谢谢……我刚才还被这个问题困扰了几分钟,哈哈。 - Stan R.

1
大多数答案可能存在相同的缺陷。给定一个空文本,它们将不会产生任何结果。我们(我)期望至少返回空字符串(与在字符串中没有出现的字符上分割的行为相同,它将返回一个项目:给定的字符串)。
因此,我们应该至少循环所有时间(基于Jon的代码):
IEnumerable<string> SplitIntoChunks (string text, int chunkSize)
{
    int offset = 0;
    do
    {
        int size = Math.Min (chunkSize, text.Length - offset);
        yield return text.Substring (offset, size);
        offset += size;
    } while (offset < text.Length);
}

或者使用 for 循环(编辑:经过一些尝试,我发现了更好的处理方式,可以处理 chunkSize 大于文本长度的情况):

IEnumerable<string> SplitIntoChunks (string text, int chunkSize)
{
    if (text.Length <= chunkSize)
        yield return text;
    else
    {
        var chunkCount = text.Length / chunkSize;
        var remainingSize = text.Length % chunkSize;

        for (var offset = 0; offset < chunkCount; ++offset)
            yield return text.Substring (offset * chunkSize, chunkSize);

        // yield remaining text if any
        if (remainingSize != 0)
            yield return text.Substring (chunkCount * chunkSize, remainingSize);
    }
}

那也可以与do/while循环一起使用 ;)


0

通用扩展方法:

using System;
using System.Collections.Generic;
using System.Linq;

public static class IEnumerableExtensions
{
  public static IEnumerable<IEnumerable<T>> SplitToChunks<T> (this IEnumerable<T> coll, int chunkSize)
  {
    int skipCount = 0;
    while (coll.Skip (skipCount).Take (chunkSize) is IEnumerable<T> part && part.Any ())
    {
      skipCount += chunkSize;
      yield return part;
    }
  }
}

class Program
{
  static void Main (string[] args)
  {
    var col = Enumerable.Range(1,1<<10);
    var chunks = col.SplitToChunks(8);

    foreach (var c in chunks.Take (200))
    {
      Console.WriteLine (string.Join (" ", c.Select (n => n.ToString ("X4"))));
    }

    Console.WriteLine ();
    Console.WriteLine ();

    "Split this text into parts that are fifteen characters in length, surrounding each part with single quotes and output each into the console on seperate lines."
      .SplitToChunks (15)
      .Select(p => $"'{string.Concat(p)}'")
      .ToList ()
      .ForEach (p => Console.WriteLine (p));

    Console.ReadLine ();
  }
}

-1

类似这样的吗?

Calculate eachLength = StringLength / WantedCharLength
Then for (int i = 0; i < StringLength; i += eachLength)
SubString (i, eachLength);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接