Разделенная строка в 512 чанках
Вопрос
Может быть, основной вопрос, но допустим, у меня есть строка длиной 2000 символов, мне нужно разбить эту строку на максимум 512 символов каждый.
Есть ли хороший способ, например, цикл или так для этого?
Решение
Примерно так:
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, которые, в свою очередь, могут не совпадать с разбиением на куски глифов.
Другие советы
Хотя на этот вопрос есть приемлемый ответ, вот короткая версия с помощью регулярных выражений. Пуристам это может не понравиться (по понятным причинам), но когда вам нужно быстрое решение, и вы разбираетесь с регулярными выражениями, это может быть так. Производительность довольно хорошая, на удивление:
string [] split = Regex.Split(yourString, @"(?<=\G.{512})");
Что это делает? Отрицательный взгляд назад и запоминание последней позиции с помощью \G
. Он также поймает последний бит, даже если он не делится на 512.
используя реализацию Джона и ключевое слово 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);
}
}
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));
}
Я позволю себе предоставить более LINQified версию решения Джона, основанную на том факте, что тип 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;
}
У большинства ответов может быть тот же недостаток. При пустом тексте они ничего не дадут. Мы (I) ожидаем, что, по крайней мере, вернем эту пустую строку (такое же поведение, что и при разбиении на символ, не входящий в строку, который вернет один элемент: данную строку)
поэтому мы должны выполнить цикл хотя бы один раз (на основе кода Джона):
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;)
Общий метод расширения:
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 ();
}
}
Что-то вроде?
Calculate eachLength = StringLength / WantedCharLength
Then for (int i = 0; i < StringLength; i += eachLength)
SubString (i, eachLength);