Tách một chuỗi thành các khối có kích thước nhất định


218

Giả sử tôi có một chuỗi:

string str = "1111222233334444"; 

Làm thế nào tôi có thể phá vỡ chuỗi này thành khối có kích thước?

ví dụ: phá vỡ điều này thành kích thước của 4 sẽ trả về chuỗi:

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

18
Tại sao nên sử dụng LINQ hoặc regexes khi các hàm thao tác chuỗi tiêu chuẩn của C # có thể thực hiện việc này với ít nỗ lực hơn và tốc độ cao hơn? Ngoài ra, điều gì xảy ra nếu chuỗi có số ký tự lẻ có độ dài?
Ian Kemp

7
"Tôi muốn tránh các vòng lặp" - tại sao?
Mitch Wheat

12
Sử dụng một vòng lặp đơn giản chắc chắn là những gì mang lại hiệu quả tốt nhất.
Guffa

4
nichesoftware.co.nz/blog/200909/linq-vs-loop-performance là một so sánh khá tốt giữa linq và vòng lặp thực tế trên một mảng. Tôi nghi ngờ bạn sẽ không bao giờ tìm thấy linq nhanh hơn mã được viết thủ công bởi vì nó liên tục gọi các đại biểu trong thời gian chạy khó có thể tối ưu hóa. Linq vui hơn mặc dù :)
Blindy

2
Cho dù bạn đang sử dụng LINQ hay regexes, vòng lặp vẫn còn đó.
Anton Tykhyy

Câu trả lời:


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

Xin lưu ý rằng mã bổ sung có thể được yêu cầu để xử lý một cách duyên dáng các trường hợp cạnh ( nullhoặc chuỗi đầu vào trống chunkSize == 0, độ dài chuỗi đầu vào không chia hết cho chunkSize, v.v.). Câu hỏi ban đầu không chỉ định bất kỳ yêu cầu nào cho các trường hợp cạnh này và trong thực tế, các yêu cầu có thể thay đổi để chúng nằm ngoài phạm vi của câu trả lời này.


3
@Hỏ tốt bắt! Điều này có thể được khắc phục bằng một biểu thức ternary thả xuống trên tham số đếm của chuỗi con. Một cái gì đó như : (i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize. Một vấn đề khác là chức năng này không giải thích cho str là null. Điều này có thể được khắc phục bằng cách gói toàn bộ câu lệnh return trong một biểu thức ternary khác : (str != null) ? ... : Enumerable.Empty<String>();.
Drew Spickes

7
Điều này rất gần, nhưng không giống như 30 lần nâng cấp trước đó, tôi đã phải thay đổi giới hạn số vòng lặp của Phạm vi từ str.Length / chunkSizesangdouble length = str.Length; double size = chunkSize; int count = (int)Math.Ceiling(length/size); return Enumerable.Range(0, count)...
khoảng cách

4
@KonstantinSpirin Tôi đồng ý nếu mã hoạt động. Nó chỉ xử lý trường hợp một chuỗi là bội số của chunkSize, phần còn lại của chuỗi bị mất. Xin hãy ủng hộ. Ngoài ra, hãy nhớ rằng LINQ và phép thuật của nó không dễ hiểu đối với người chỉ muốn xem xét một giải pháp cho vấn đề này. Bây giờ một người phải hiểu các hàm Enumerable.Range () và .Select () làm gì. Tôi sẽ không tranh luận rằng bạn nên hiểu điều đó để viết mã C # /. NET vì các hàm này đã có trong BCL trong nhiều năm nay.
CodeMonkeyKing

6
Chủ đề bắt đầu nói trong ý kiến ​​rằng StringLength % 4 will always be 0. Nếu Linqkhông dễ hiểu thì có những câu trả lời khác sử dụng vòng lặp và sản lượng. Bất cứ ai cũng được tự do lựa chọn giải pháp cô ấy thích nhất. Bạn có thể đăng mã của mình dưới dạng câu trả lời và mọi người sẽ vui vẻ bỏ phiếu cho nó.
Konstantin Spirin

3
Enumerable.Range (0, (str.Lạng + chunkSize - 1) / chunkSize) .Select (i => str.Subopes (i * chunkSize, Math.Min (str.Lạng - i * chunkSize, chunkSize)))
Sten Petrov

135

Trong sự kết hợp của câu trả lời của bồ câu + Konstatin ...

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

Điều này sẽ làm việc cho tất cả các chuỗi có thể được chia thành một số lượng lớn và sẽ ném một ngoại lệ khác.

Nếu bạn muốn hỗ trợ các chuỗi có độ dài bất kỳ, bạn có thể sử dụng mã sau đây:

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

Tuy nhiên, OP tuyên bố rõ ràng rằng anh ta không cần điều này; Nó hơi dài và khó đọc hơn, chậm hơn một chút. Theo tinh thần của KISS và YAGNI, tôi sẽ chọn tùy chọn đầu tiên: đó có thể là cách triển khai hiệu quả nhất có thể, và nó rất ngắn, dễ đọc và quan trọng là ném ra một ngoại lệ cho đầu vào không phù hợp.


4
+1 giá trị một cái gật đầu. kinda nhấn móng tay trên đầu. anh ấy đang tìm kiếm sytnax cô đọng và bạn cũng cho hiệu suất tốt hơn (có thể).
bồ câu

7
Và nếu bạn làm cho nó "tĩnh ... Chunk (chuỗi str này, int chunkSize) {" bạn thậm chí còn có thêm một C # -Feature mới nữa. Sau đó, bạn có thể viết "1111222233334444" .Chunk (4).
MartinStettner

1
@MartinStettner: Đó chắc chắn là một ý tưởng hay nếu đây là một hoạt động phổ biến.
Eamon Nerbonne

Bạn chỉ nên bao gồm mã sau. Cái trước đòi hỏi bạn phải hiểu và kiểm tra xem chuỗi có phải là bội số của kích thước khối trước khi sử dụng hay không, hoặc hiểu rằng nó sẽ không trả về phần còn lại của chuỗi.
CodeMonkeyKing

Câu hỏi của OP không làm rõ liệu anh ta có cần chức năng đó hay không. Giải pháp đầu tiên đơn giản hơn, nhanh hơn và thất bại đáng tin cậy với một ngoại lệ nếu chuỗi không thể chia đều thành kích thước khối đã chỉ định. Tôi đồng ý rằng việc trả lại kết quả "sai" sẽ rất tệ, nhưng đó không phải là điều nó xảy ra - nó chỉ đưa ra một ngoại lệ, vì vậy tôi sẽ ổn khi sử dụng nó nếu bạn có thể sống với giới hạn.
Eamon Nerbonne

56

Tại sao không phải là vòng lặp? Đây là một cái gì đó sẽ làm điều đó khá tốt:

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

Tôi không biết làm thế nào bạn đối phó với trường hợp chuỗi không phải là yếu tố 4, nhưng không nói rằng ý tưởng của bạn là không thể, chỉ tự hỏi động lực cho nó nếu một vòng lặp đơn giản làm điều đó rất tốt? Rõ ràng những điều trên có thể được làm sạch và thậm chí đưa vào như một phương pháp mở rộng.

Hoặc như đã đề cập trong các bình luận, bạn biết đó là / 4 rồi

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

1
Bạn có thể kéo ra int chunkSize = 4ngoài vòng lặp. Nó sẽ chỉ được sửa đổi trên đường chuyền cuối cùng.
John Women'sella

+1 cho một giải pháp đơn giản và hiệu quả - đây là cách tôi sẽ thực hiện nó, mặc dù tôi đã sử dụng i += chunkSizethay thế.
Ian Kemp

Có lẽ là một ngụy biện nhỏ, nhưng có lẽ bạn cũng nên kéo str.Lengthra khỏi vòng lặp và vào một biến cục bộ. Trình tối ưu hóa C # thể có độ dài mảng nội tuyến, nhưng tôi nghĩ mã được viết sẽ thực hiện một cuộc gọi phương thức trên mỗi vòng lặp, không hiệu quả, vì kích thước strkhông bao giờ thay đổi.
Daniel Pryden

@Daniel, đưa ý tưởng của bạn vào đó. mặc dù tôi không chắc chắn rằng điều này sẽ không được tính trong thời gian chạy, nhưng đó là một câu hỏi khác;)
dove

@Daniel trở lại vấn đề này, khá chắc chắn rằng tối ưu hóa này sẽ được trình biên dịch trích xuất.
bồ câu

41

Sử dụng biểu thức chính quyLinq :

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

Tôi thấy điều này dễ đọc hơn, nhưng đó chỉ là ý kiến ​​cá nhân. Nó cũng có thể là một lót :).


7
Thay đổi mẫu thành @ "\ d {1,4}" và nó hoạt động với mọi độ dài chuỗi. :)
Guffa

3
+1 Mặc dù điều này chậm hơn các giải pháp khác, nhưng nó chắc chắn rất dễ đọc. Tôi không rõ liệu OP yêu cầu chữ số hay ký tự tùy ý; Có lẽ nên khôn ngoan thay thế \dlớp nhân vật bằng một .và chỉ định RegexOptions.Singleline.
Eamon Nerbonne

2
hoặc chỉ Regex.Matches (s, @ "\ d {1,4}"). Chọn (m => m.Value) .ToList (); Tôi chưa bao giờ hiểu được cú pháp thay thế này chỉ phục vụ để làm xáo trộn rằng chúng tôi đang sử dụng các phương thức mở rộng.
Dag

38

Điều này dựa trên giải pháp @dove nhưng được triển khai như một phương thức mở rộng.

Những lợi ích:

  • Phương pháp mở rộng
  • Bao gồm các trường hợp góc
  • Chia chuỗi với bất kỳ ký tự nào: số, chữ cái, ký hiệu khác

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

Sử dụng

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

Các bài kiểm tra đơn vị được gỡ bỏ cho ngắn gọn (xem bản sửa đổi trước đó )


Giải pháp thú vị, nhưng để tránh việc kiểm tra vượt quá null trên đầu vào, có vẻ hợp lý hơn khi cho phép một chuỗi trống chỉ trả về một phần chuỗi trống duy nhất:if (str.Length == 0) yield return String.Empty; else { for... }
Nyerguds

Ý tôi là, đó là cách String.Split bình thường xử lý các chuỗi rỗng; nó trả về một mục nhập chuỗi rỗng.
Nyerguds

Lưu ý bên: ví dụ sử dụng của bạn là sai. Bạn không thể IEnumerablechuyển sang mảng, đặc biệt là không ngầm.
Nyerguds

Cá nhân tôi thích gọi phương pháp đó Chunkify.. Nó không phải của tôi, tôi không nhớ tôi đã thấy cái tên đó ở đâu, nhưng tôi cảm thấy rất tuyệt
quetzalcoatl

20

Làm thế nào điều này cho một lót?

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

Với regex này, không có vấn đề gì nếu đoạn cuối cùng có ít hơn bốn ký tự, bởi vì nó chỉ nhìn vào các ký tự đằng sau nó.

Tôi chắc chắn đây không phải là giải pháp hiệu quả nhất, nhưng tôi phải ném nó ra khỏi đó.


trong trường hợp target.Lenght % ChunckSize == 0lợi nhuận mà một thêm trống hàng ví dụList<string> result = new List<string>(Regex.Split("fooo", @"(?<=\G.{4})", RegexOptions.Singleline));
Fubo

9

Nó không đẹp và không nhanh, nhưng nó hoạt động, nó là một lớp lót và đó là 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();

Có đảm bảo rằng GroupBy duy trì trật tự các yếu tố không?
Konstantin Spirin

ToCharArraylà không cần thiết vì stringIEnumerable<char>.
juharr

8

Gần đây tôi đã phải viết một cái gì đó thực hiện điều này trong công việc, vì vậy tôi nghĩ rằng tôi sẽ đăng giải pháp của mình cho vấn đề này. Là một phần thưởng bổ sung, chức năng của giải pháp này cung cấp một cách để phân tách chuỗi theo hướng ngược lại và nó xử lý chính xác các ký tự unicode như Marvin Pinto đã đề cập ở trên. Vì vậy, đây là:

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;
        }
    }
}

Ngoài ra, đây là một liên kết hình ảnh đến kết quả của việc chạy mã này: http://i.imgur.com/16Iih.png


1
Tôi nhận thấy một vấn đề với mã này. Bạn có {str.ToString()}ở cuối câu lệnh IF đầu tiên. Bạn có chắc là bạn không cố ý str.String? Tôi đã có một vấn đề với mã ở trên, thực hiện thay đổi đó và mọi thứ đều hoạt động.
gunr2171

@ gunr2171 Có vẻ như nếu str == null, dòng đó cũng sẽ cung cấp một NullReferenceException.
John Zabroski

5

Điều này sẽ nhanh hơn và hiệu quả hơn nhiều so với sử dụng LINQ hoặc các phương pháp khác được sử dụng ở đây.

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

Điều này có vẻ như nó kiểm tra sớm, nhưng nó không. Bạn không nhận được một lỗi cho đến khi bạn bắt đầu liệt kê vô số. Bạn cần chia chức năng của mình thành hai phần, trong đó phần đầu tiên thực hiện kiểm tra đối số và sau đó trả về kết quả của phần thứ hai, phần riêng thực hiện phép liệt kê.
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

Bạn có thể sử dụng morelinq của Jon Skeet. Sử dụng hàng loạt như:

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

Điều này sẽ trả về 4 khối cho chuỗi "1111222233334444". Nếu độ dài chuỗi nhỏ hơn hoặc bằng kích thước khối Batchsẽ trả về chuỗi là phần tử duy nhất củaIEnumerable<string>

Đối với đầu ra:

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

và nó sẽ cho:

1111
2222
3333
4444

Trong số các tác giả của MoreLINEQ tôi thấy Jonathan Skeet , nhưng không có Jon Skeet . Vì vậy, bạn đã có nghĩa là các Jon Skeet, hoặc những gì? ;-)
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()));
}

và một cách tiếp cận khác:

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

Sáu năm sau o_O

Chỉ vì

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

hoặc là

    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 tất cả các trường hợp cạnh được xử lý.

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

Điều gì về trường hợp cạnh "đầu vào là một chuỗi rỗng"? Tôi hy vọng rằng, giống như với Split, sẽ trả về một IEnumerable với một mục nhập chứa chuỗi rỗng.
Nyerguds

3

Đơn giản và ngắn gọn:

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

Tại sao không sử dụng .?
soái ca

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;
}

Nó xử lý chính xác độ dài chuỗi đầu vào không chia hết cho chunkSize.

Xin lưu ý rằng mã bổ sung có thể được yêu cầu để xử lý một cách duyên dáng các trường hợp cạnh (chuỗi đầu vào rỗng hoặc rỗng, chunkSize == 0).


2

Một mẹo quan trọng nếu chuỗi đang được phân đoạn cần hỗ trợ tất cả các ký tự Unicode.

Nếu chuỗi là để hỗ trợ các ký tự quốc tế như thế 𠀋, thì hãy tách chuỗi bằng cách sử dụng lớp System.Globalization.StringInfo. Sử dụng StringInfo, bạn có thể tách chuỗi dựa trên số phần tử văn bản.

string internationalString = '𠀋';

Chuỗi trên có Độ dài là 2, vì thuộc String.Lengthtính trả về số lượng đối tượng Char trong trường hợp này, chứ không phải số lượng ký tự Unicode.


2

Câu trả lời hay nhất, dễ nhất và chung chung :).

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

Tính toán độ dài ở dòng cuối cùng là dự phòng, chỉ cần sử dụng mức Substringquá tải không yêu cầu tham số độ dài originalString.Substring(i). Ngoài ra, bạn có thể sử dụng >thay vì >=trong kiểm tra của bạn.
Racil Hilan

@RacilHilan Tôi sẽ kiểm tra các thay đổi mã với đề xuất của bạn và cập nhật câu trả lời. Tôi rất vui vì ai đó có danh tiếng tốt như vậy đã có thời gian để xem lại mã của tôi. :) Cảm ơn, Sandeep
Sandeep Kushwah

2

Cá nhân tôi thích giải pháp của mình :-)

Nó xử lý:

  • Độ dài chuỗi là bội số của kích thước khối.
  • Độ dài chuỗi KHÔNG phải là bội số của kích thước khối.
  • Độ dài chuỗi nhỏ hơn kích thước khối.
  • NULL và chuỗi rỗng (ném một ngoại lệ).
  • Kích thước chunk nhỏ hơn 1 (ném một ngoại lệ).

Nó được thực hiện như một phương thức mở rộng và nó tính toán số lượng khối sẽ tạo ra trước đó. Nó kiểm tra đoạn cuối bởi vì trong trường hợp độ dài văn bản không phải là bội số thì nó cần phải ngắn hơn. Sạch sẽ, ngắn gọn, dễ hiểu ... và hoạt động!

    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
Tôi thích câu trả lời này rất nhiều, nhưng có lẽ bạn nên sử dụng if ((i + 1) * chunk> = input.Ldrops) thay vì thử / bắt vì các trường hợp ngoại lệ dành cho các trường hợp ngoại lệ.
nelsontruran

2

Tôi nghĩ rằng đây là một câu trả lời thẳng về phía trước:

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

Và nó bao gồm các trường hợp cạnh.


2

Tôi biết câu hỏi đã có tuổi, nhưng đây là một triển khai Rx. Nó xử lý length % chunkSize != 0vấn đề ra khỏi hộp:

   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

Tôi đã xây dựng một chút về giải pháp của João. Những gì tôi đã làm khác nhau là trong phương thức của tôi, bạn thực sự có thể chỉ định xem bạn muốn trả về mảng với các ký tự còn lại hay bạn muốn cắt bớt chúng nếu các ký tự kết thúc không khớp với độ dài đoạn yêu cầu của bạn, tôi nghĩ nó khá linh hoạt và mã khá đơn giản:

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;
    }

Bạn, eh, quên tham số MaxLạng.
Nyerguds

1

Thay đổi một chút để trả về các phần có kích thước không bằng 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;
    }

Không chắc tôi thấy việc sử dụng back-đúc mà Listđến IEnumerable; tất cả những gì làm là ẩn các chức năng cụ thể của Danh sách mà bạn có thể muốn sử dụng. Không có bất kỳ nhược điểm nào khi chỉ trả lại List.
Nyerguds

1

Tôi không thể nhớ ai đã cho tôi cái này, nhưng nó hoạt động rất tốt. Tôi đã kiểm tra tốc độ một số cách để chia nhiều loại thành nhiều nhóm. Việc sử dụng sẽ như thế này ...

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

Mã mở rộng sẽ trông như thế này ...

#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;
    }
}

Bạn có thể cải thiện một chút mã của mình: chuyển biểu thức gia tăng i += offSetthành forbiểu thức của bạn .
JimiLoe

1

Sửa đổi (bây giờ nó chấp nhận bất cứ rỗng không stringbất kỳ dương chunkSize) Konstantin Spirin 's giải pháp:

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

Các xét nghiệm:

  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;
}

bản giới thiệu


cái này giữ phần còn lại của chuỗi (chia bài) thậm chí nó còn ngắn hơn "chunkLenght", cảm ơn
Jason Loki Smith

0

Dựa trên các câu trả lời của người đăng khác, cùng với một số mẫu sử dụng:

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

Sử dụng các phần mở rộng Bộ đệm từ thư viện IX

    static IEnumerable<string> Split( this string str, int chunkSize )
    {
        return str.Buffer(chunkSize).Select(l => String.Concat(l));
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.