Cách dễ nhất để phân tách một chuỗi trên dòng mới trong .NET?


806

Tôi cần chia một chuỗi thành các dòng mới trong .NET và cách duy nhất tôi biết để phân tách chuỗi là với phương thức Split . Tuy nhiên, điều đó sẽ không cho phép tôi (dễ dàng) phân chia trên một dòng mới, vậy cách tốt nhất để làm điều đó là gì?


2
Tại sao nó không? Chỉ cần chia nhỏ trên System.En
Môi trường.NewLine

16
Nhưng bạn phải gói nó trong một chuỗi [] và thêm một đối số phụ và ... nó chỉ cảm thấy lộn xộn.
RCIX

Câu trả lời:


1414

Để phân chia trên một chuỗi, bạn cần sử dụng quá tải cần một mảng các chuỗi:

string[] lines = theText.Split(
    new[] { Environment.NewLine },
    StringSplitOptions.None
);

Chỉnh sửa:
Nếu bạn muốn xử lý các loại ngắt dòng khác nhau trong một văn bản, bạn có thể sử dụng khả năng khớp nhiều hơn một chuỗi. Điều này sẽ phân chia chính xác trên một trong hai loại ngắt dòng và bảo toàn các dòng trống và khoảng cách trong văn bản:

string[] lines = theText.Split(
    new[] { "\r\n", "\r", "\n" },
    StringSplitOptions.None
);

3
@RCIX: Gửi các tham số chính xác cho phương thức này hơi khó xử vì bạn đang sử dụng nó cho một cái gì đó đơn giản hơn nhiều so với khả năng của nó. Ít nhất là ở đó, trước khung 2, bạn phải sử dụng biểu thức chính quy hoặc xây dựng thói quen chia tách của riêng bạn để phân tách trên một chuỗi ...
Guffa

4
@Leandro: Environment.NewLineThuộc tính chứa dòng mới mặc định cho hệ thống. Đối với một hệ thống Windows chẳng hạn "\r\n".
Guffa

3
@Leandro: Một dự đoán sẽ là chương trình tách ra khi \nđể lại một dòng \rở cuối mỗi dòng, sau đó xuất ra các dòng có một \r\ngiữa chúng.
Guffa

3
@Samuel: Các chuỗi thoát \r\nthoát (trong số các chuỗi khác) có ý nghĩa đặc biệt đối với trình biên dịch C #. VB không có các chuỗi thoát đó, vì vậy có các hằng số được sử dụng thay thế.
Guffa

2
Nếu bạn muốn chấp nhận các tệp từ nhiều hệ điều hành khác nhau, bạn cũng có thể thêm "\ n \ r" vào đầu và "\ r" vào cuối danh sách dấu phân cách. Tôi không chắc chắn rằng nó có giá trị hiệu suất hit mặc dù. ( en.wikipedia.org/wiki/Newline )
user420667

121

Còn việc sử dụng a StringReaderthì sao?

using (System.IO.StringReader reader = new System.IO.StringReader(input)) {
    string line = reader.ReadLine();
}

13
Đây là yêu thích của tôi. Tôi đã gói gọn trong một phương thức mở rộng và mang lại dòng hiện tại: gist.github.com/ronnieoverby/7916886
Ronnie Overby

3
Đây là giải pháp phi regex duy nhất tôi tìm thấy cho .netcf 3.5
Carl

8
Đặc biệt tốt khi đầu vào lớn và sao chép toàn bộ vào một mảng trở nên chậm / tốn nhiều bộ nhớ.
Alejandro

1
Như đã viết, câu trả lời này chỉ đọc dòng đầu tiên. Xem câu trả lời của Steve Cooper cho whilevòng lặp nên được thêm vào câu trả lời này.
ToolmakerSteve

48

Bạn sẽ có thể phân tách chuỗi của bạn khá dễ dàng, như vậy:

aString.Split(Environment.NewLine.ToCharArray());

46
Trên một hệ thống không * nix sẽ phân tách trên các ký tự riêng biệt trong chuỗi Newline, tức là các ký tự CR và LF. Điều đó sẽ gây ra một chuỗi trống thêm giữa mỗi dòng.
Guffa

Chỉnh sửa cho tôi nếu tôi sai, nhưng sẽ không phân chia các ký tự \ và n chứ?
RCIX

7
@RCIX: Không, mã \ r và \ n đại diện cho các ký tự đơn. Chuỗi "\ r \ n" là hai ký tự, không phải bốn.
Guffa

10
nếu bạn thêm tham số StringSplitOptions.RemoveEmptyEntries, thì điều này sẽ hoạt động hoàn hảo.
Ruben

18
@Ruben: Không, nó sẽ không. Serge đã gợi ý rằng trong câu trả lời của anh ấy, và tôi đã giải thích rằng nó cũng sẽ xóa các dòng trống trong văn bản gốc cần được bảo tồn.
Guffa

34

Cố gắng tránh sử dụng chuỗi.Split cho một giải pháp chung, bởi vì bạn sẽ sử dụng nhiều bộ nhớ hơn ở mọi nơi bạn sử dụng chức năng - chuỗi gốc và bản sao tách, cả trong bộ nhớ. Hãy tin tôi rằng đây có thể là một vấn đề nghiêm trọng khi bạn bắt đầu mở rộng quy mô - chạy ứng dụng xử lý hàng loạt 32 bit xử lý các tài liệu 100MB và bạn sẽ giải quyết được tám luồng đồng thời. Không phải là tôi đã từng đến đó trước đây ...

Thay vào đó, sử dụng một trình vòng lặp như thế này;

    public static IEnumerable<string> SplitToLines(this string input)
    {
        if (input == null)
        {
            yield break;
        }

        using (System.IO.StringReader reader = new System.IO.StringReader(input))
        {
            string line;
            while( (line = reader.ReadLine()) != null)
            {
                yield return line;
            }
        }
    }

Điều này sẽ cho phép bạn thực hiện một vòng lặp hiệu quả hơn về bộ nhớ xung quanh dữ liệu của bạn;

foreach(var line in document.SplitToLines()) 
{
    // one line at a time...
}

Tất nhiên, nếu bạn muốn tất cả trong bộ nhớ, bạn có thể làm điều này;

var allTheLines = document.SplitToLines.ToArray();

Tôi đã ở đó ... (phân tích các tệp HTML lớn và hết bộ nhớ). Có, tránh chuỗi.Split. Sử dụng chuỗi.Split có thể dẫn đến việc sử dụng Heap đối tượng lớn (LOH) - nhưng tôi không chắc chắn 100% về điều đó.
Peter Mortensen

Nếu bạn đã tạo SplitToLines một phương thức tĩnh (có vẻ như bạn là dd), thì bạn có thể làm blah.SplitToLines.. như document.SplitToLines...thế nào?
barlop

ah Tôi thấy bạn đưa thisvào các tham số chính thức làm cho nó trở thành một phương thức mở rộng.
barlop

26

Dựa trên câu trả lời của Guffa, trong một lớp mở rộng, sử dụng:

public static string[] Lines(this string source) {
    return source.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
}

9

Đối với một biến chuỗi s:

s.Split(new string[]{Environment.NewLine},StringSplitOptions.None)

Điều này sử dụng định nghĩa môi trường của bạn về kết thúc dòng. Trên Windows, các kết thúc dòng là CR-LF (trả về vận chuyển, nguồn cấp dữ liệu) hoặc trong các ký tự thoát của C # \r\n.

Đây là một giải pháp đáng tin cậy, bởi vì nếu bạn kết hợp lại các dòng với String.Join, thì điều này bằng với chuỗi ban đầu của bạn:

var lines = s.Split(new string[]{Environment.NewLine},StringSplitOptions.None);
var reconstituted = String.Join(Environment.NewLine,lines);
Debug.Assert(s==reconstituted);

Những gì không làm:

  • Sử dụng StringSplitOptions.RemoveEmptyEntries, bởi vì điều này sẽ phá vỡ đánh dấu, chẳng hạn như Markdown, nơi các dòng trống có mục đích cú pháp.
  • Tách trên dấu phân cách new char[]{Environment.NewLine}, bởi vì trên Windows, điều này sẽ tạo ra một phần tử chuỗi trống cho mỗi dòng mới.

Về cơ bản câu trả lời tương tự ở đây là đánh giá hàng đầu, được chấp nhận, nhưng nó có một bài kiểm tra đơn vị tốt và cẩn thận.
vapcguy

8

Regex cũng là một lựa chọn:

    private string[] SplitStringByLineFeed(string inpString)
    {
        string[] locResult = Regex.Split(inpString, "[\r\n]+");
        return locResult;
    }

7
Nếu bạn muốn khớp chính xác các dòng, giữ nguyên các dòng trống, chuỗi regex này sẽ tốt hơn : "\r?\n".
Rory O'Kane

7

Tôi chỉ nghĩ rằng tôi sẽ thêm hai bit của mình, bởi vì các giải pháp khác cho câu hỏi này không thuộc phân loại mã có thể sử dụng lại và không thuận tiện.

Khối mã sau đây mở rộng stringđối tượng để nó có sẵn như một phương thức tự nhiên khi làm việc với các chuỗi.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Collections.ObjectModel;

namespace System
{
    public static class StringExtensions
    {
        public static string[] Split(this string s, string delimiter, StringSplitOptions options = StringSplitOptions.None)
        {
            return s.Split(new string[] { delimiter }, options);
        }
    }
}

Bây giờ bạn có thể sử dụng .Split()hàm từ bất kỳ chuỗi nào như sau:

string[] result;

// Pass a string, and the delimiter
result = string.Split("My simple string", " ");

// Split an existing string by delimiter only
string foo = "my - string - i - want - split";
result = foo.Split("-");

// You can even pass the split options parameter. When omitted it is
// set to StringSplitOptions.None
result = foo.Split("-", StringSplitOptions.RemoveEmptyEntries);

Để phân chia trên một ký tự dòng mới, chỉ cần vượt qua "\n"hoặc "\r\n"làm tham số dấu phân cách.

Nhận xét: Sẽ thật tuyệt nếu Microsoft thực hiện quá tải này.


Environment.Newlineđược ưu tiên để mã hóa cứng \nhoặc \r\n.
Michael Blackburn

3
@MichaelBlackburn - Đó là một tuyên bố không hợp lệ vì không có ngữ cảnh. Environment.Newlinedành cho khả năng tương thích đa nền tảng, không phải để làm việc với các tệp sử dụng các kết thúc dòng khác với hệ điều hành hiện tại. Xem ở đây để biết thêm thông tin , vì vậy nó thực sự phụ thuộc vào những gì nhà phát triển đang làm việc với. Việc sử dụng Environment.Newlineđảm bảo không có sự thống nhất trong loại trả về dòng giữa các hệ điều hành, trong đó 'mã hóa cứng' mang lại cho nhà phát triển toàn quyền kiểm soát.
Kraang Prime

2
@MichaelBlackburn - Không cần bạn phải thô lỗ. Tôi chỉ đơn thuần là cung cấp thông tin. .Newlinekhông phải là phép thuật, dưới mui xe, nó chỉ là các chuỗi như được cung cấp ở trên dựa trên một công tắc nếu nó đang chạy trên unix hoặc trên các cửa sổ. Đặt cược an toàn nhất, trước tiên là thực hiện thay thế một chuỗi cho tất cả "\ r \ n" và sau đó phân tách trên "\ n". Trường hợp sử dụng .Newlinekhông thành công, là khi bạn đang làm việc với các tệp được lưu bởi các chương trình khác sử dụng một phương pháp khác để ngắt dòng. Nó hoạt động tốt nếu bạn biết mỗi khi tệp đọc luôn sử dụng ngắt dòng của hệ điều hành hiện tại của bạn.
Kraang Prime

Vì vậy, những gì tôi nghe được là cách dễ đọc nhất (có thể sử dụng bộ nhớ cao hơn) foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');. Tôi có hiểu chính xác rằng điều này hoạt động trên tất cả các nền tảng?
John Doe

4

Tôi hiện đang sử dụng chức năng này (dựa trên các câu trả lời khác) trong VB.NET:

Private Shared Function SplitLines(text As String) As String()
    Return text.Split({Environment.NewLine, vbCrLf, vbLf}, StringSplitOptions.None)
End Function

Nó cố gắng phân tách trên dòng mới nền tảng cục bộ trước, và sau đó rơi trở lại từng dòng mới có thể.

Tôi chỉ cần điều này trong một lớp cho đến nay. Nếu điều đó thay đổi, tôi có thể sẽ thực hiện điều này Publicvà chuyển nó sang một lớp tiện ích, và thậm chí có thể biến nó thành một phương thức mở rộng.

Đây là cách tham gia các dòng sao lưu, để có biện pháp tốt:

Private Shared Function JoinLines(lines As IEnumerable(Of String)) As String
    Return String.Join(Environment.NewLine, lines)
End Function

@Samuel - lưu ý các trích dẫn. Họ thực sự có ý nghĩa đó. "\r"= trả lại. "\r\n"= trả lại + dòng mới. (vui lòng xem lại bài đăng này và giải pháp được chấp nhận tại đây
Kraang Prime

@Kraang Hmm .. Tôi đã không làm việc với .NET trong một thời gian dài. Tôi sẽ ngạc nhiên nếu nhiều người bình chọn một câu trả lời sai. Tôi thấy rằng tôi cũng nhận xét về câu trả lời của Guffa và đã làm rõ ở đó. Tôi đã xóa bình luận của tôi cho câu trả lời này. Cảm ơn cho những người đứng đầu lên.
Samuel

2

Vâng, thực sự phân chia nên làm:

//Constructing string...
StringBuilder sb = new StringBuilder();
sb.AppendLine("first line");
sb.AppendLine("second line");
sb.AppendLine("third line");
string s = sb.ToString();
Console.WriteLine(s);

//Splitting multiline string into separate lines
string[] splitted = s.Split(new string[] {System.Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);

// Output (separate lines)
for( int i = 0; i < splitted.Count(); i++ )
{
    Console.WriteLine("{0}: {1}", i, splitted[i]);
}

2
Tùy chọn RemoveEmptyEntries sẽ xóa các dòng trống khỏi văn bản. Điều đó có thể là mong muốn trong một số tình huống, nhưng một sự phân chia rõ ràng sẽ giữ được các dòng trống.
Guffa

vâng, bạn nói đúng, tôi chỉ đưa ra giả định này, rằng ...
à

1
string[] lines = text.Split(
  Environment.NewLine.ToCharArray(), 
  StringSplitOptions.RemoveEmptyStrings);

Các RemoveEmptyStrings tùy chọn sẽ đảm bảo bạn không có mục rỗng do \ n sau một \ r

(Chỉnh sửa để phản ánh ý kiến ​​:) Lưu ý rằng nó cũng sẽ loại bỏ các dòng trống chính hãng trong văn bản. Đây thường là những gì tôi muốn nhưng nó có thể không phải là yêu cầu của bạn.


Các tùy chọn RemoveEmptyStrings cũng sẽ xóa các dòng trống, do đó, nó không hoạt động đúng nếu văn bản có các dòng trống trong đó.
Guffa

Bạn có thể muốn giữ các dòng trống chính hãng: \ r \ n \ r \ n
mỏng

0

Tôi không biết về Môi trường. Mới, nhưng tôi đoán đây là một giải pháp rất tốt.

Cố gắng của tôi sẽ là:

        string str = "Test Me\r\nTest Me\nTest Me";
        var splitted = str.Split('\n').Select(s => s.Trim()).ToArray();

.Trim bổ sung sẽ xóa mọi \ r hoặc \ n có thể vẫn còn (ví dụ: khi ở trên windows nhưng tách một chuỗi với các ký tự os x newline). Có lẽ không phải là phương pháp nhanh nhất.

BIÊN TẬP:

Như các ý kiến ​​đã chỉ ra một cách chính xác, điều này cũng loại bỏ bất kỳ khoảng trắng nào ở đầu dòng hoặc trước nguồn cấp dữ liệu mới. Nếu bạn cần giữ khoảng trắng đó, hãy sử dụng một trong các tùy chọn khác.


Trim cũng sẽ loại bỏ bất kỳ khoảng trắng nào ở đầu và cuối dòng, ví dụ như thụt lề.
Guffa

".Trim xóa mọi \ r hoặc \ n có thể vẫn còn" - ouch. Tại sao không viết mã mạnh mẽ thay thế?
bzlm

Có thể tôi đã trả lời sai câu hỏi, nhưng không rõ khoảng trắng đó phải được bảo tồn. Tất nhiên bạn đúng, Trim () cũng xóa khoảng trắng.
Tối đa

1
@Max: Wow, đợi cho đến khi tôi nói với sếp rằng mã được phép làm bất cứ điều gì không được loại trừ cụ thể trong đặc tả ...;)
Guffa

-2

Câu trả lời ngớ ngẩn: ghi vào một tập tin tạm thời để bạn có thể sử dụng đáng kính File.ReadLines

var s = "Hello\r\nWorld";
var path = Path.GetTempFileName();
using (var writer = new StreamWriter(path))
{
    writer.Write(s);
}
var lines = File.ReadLines(path);

1
Tránh var, vì nó không xác định loại biến, vì vậy bạn có thể không hiểu cách sử dụng đối tượng đó hoặc đối tượng đó đại diện cho điều gì. Thêm vào đó, điều này cho thấy việc viết các dòng và thậm chí không chỉ định tên tệp, vì vậy tôi nghi ngờ nó sẽ hoạt động. Sau đó, khi đọc, đường dẫn đến tệp một lần nữa không được chỉ định. Giả sử pathlà vậy C:\Temp\test.txt, bạn nên có string[] lines = File.ReadLines(path);.
vapcguy

1
@vapcguy tôi vừa đọc cái gì? - Tôi khuyên bạn nên đọc lại bài đăng hoặc gỡ lỗi trong chương trình giao diện điều khiển vì tất cả những gì bạn nói là hoàn toàn sai | đường dẫn được đặt trên Path.GetTempFileName | var là một định nghĩa phổ biến và được đề xuất trong C # - bằng cách nó xác định loại biến ...... EDIT: Tôi không nói đây là một giải pháp tốt
koanbock

@koanbock Ok, vì vậy tôi đã tra cứu Path.GetTempFileName msdn.microsoft.com/en-us/l Library / Hay và nó nói rằng nó tạo ra một tệp không byte và trả về "đường dẫn đầy đủ của tệp đó". Tôi có thể thề rằng tôi đã thử điều này trước đây và nó đã đưa ra một ngoại lệ vì nó không tìm thấy một tập tin, nhưng thay vào đó được trả lại một vị trí thư mục. Tôi biết các đối số để sử dụng var, nhưng tôi nói rằng nó KHÔNG được khuyến khích vì nó không hiển thị đối tượng biến là gì. Nó che giấu nó.
vapcguy

-3
using System.IO;

string textToSplit;

if (textToSplit != null)
{
    List<string> lines = new List<string>();
    using (StringReader reader = new StringReader(textToSplit))
    {
        for (string line = reader.ReadLine(); line != null; line = reader.ReadLine())
        {
            lines.Add(line);
        }
    }
}

-5

Rất dễ dàng, thực sự.

VB.NET:

Private Function SplitOnNewLine(input as String) As String
    Return input.Split(Environment.NewLine)
End Function

C #:

string splitOnNewLine(string input)
{
    return input.split(environment.newline);
}

4
Hoàn toàn không chính xác và không hoạt động. Thêm vào đó, trong C #, nó Environment.NewLinegiống như trong VB.
vapcguy

Xem định danh cuối dòng trong VB.NET? cho các tùy chọn khác nhau cho dòng mới.
Peter Mortensen
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.