Cara termudah untuk membagi string pada baris baru di .NET?

806

Saya perlu membagi string menjadi baris baru di .NET dan satu-satunya cara saya tahu untuk membagi string adalah dengan metode Split . Namun itu tidak akan memungkinkan saya untuk (dengan mudah) membagi pada baris baru, jadi apa cara terbaik untuk melakukannya?

RCIX
sumber
2
Kenapa tidak? Hanya terbelah pada System.Environment.NewLine
aviraldg
16
Tetapi Anda harus membungkusnya dalam string [] dan menambahkan argumen tambahan dan ... itu terasa kikuk.
RCIX

Jawaban:

1414

Untuk membelah sebuah string, Anda perlu menggunakan overload yang membutuhkan array string:

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

Sunting:
Jika Anda ingin menangani berbagai jenis jeda baris dalam sebuah teks, Anda dapat menggunakan kemampuan untuk mencocokkan lebih dari satu string. Ini akan terpecah dengan benar pada kedua jenis jeda baris, dan mempertahankan baris kosong dan spasi dalam teks:

string[] lines = theText.Split(
    new[] { "\r\n", "\r", "\n" },
    StringSplitOptions.None
);
Guffa
sumber
3
@RCIX: Mengirim parameter yang benar ke metode ini agak canggung karena Anda menggunakannya untuk sesuatu yang jauh lebih sederhana daripada apa yang mampu dilakukannya. Setidaknya itu ada, sebelum kerangka 2 Anda harus menggunakan ekspresi reguler atau membangun rutinitas membelah Anda sendiri untuk split pada string ...
Guffa
4
@Leandro: Environment.NewLineProperti berisi baris baru default untuk sistem. Untuk sistem Windows misalnya akan "\r\n".
Guffa
3
@Leandro: Satu tebakan adalah bahwa program terbagi untuk \nmeninggalkan sebuah \rdi akhir setiap baris, kemudian menampilkan garis dengan di \r\nantara mereka.
Guffa
3
@Samuel: Urutan \rdan \nurutan (antara lain) memiliki arti khusus untuk kompiler C #. VB tidak memiliki urutan pelarian itu, jadi ada konstanta yang digunakan sebagai gantinya.
Guffa
2
Jika Anda ingin menerima file dari banyak berbagai OS, Anda juga dapat menambahkan "\ n \ r" sebagai awal dan "\ r" di akhir daftar pembatas. Saya tidak yakin itu layak untuk kinerja hit. ( en.wikipedia.org/wiki/Newline )
user420667
121

Bagaimana dengan menggunakan StringReader?

using (System.IO.StringReader reader = new System.IO.StringReader(input)) {
    string line = reader.ReadLine();
}
Sejuk
sumber
13
Ini adalah favorit saya. Saya menggunakan metode ekstensi dan menghasilkan garis pengembalian saat ini: gist.github.com/ronnieoverby/7916886
Ronnie Overby
3
Ini adalah satu-satunya solusi non-regex yang saya temukan untuk .netcf 3.5
Carl
8
Sangat bagus ketika input besar dan menyalinnya ke array menjadi lambat / intensif memori.
Alejandro
1
Seperti yang tertulis, jawaban ini hanya membaca baris pertama. Lihat jawaban Steve Cooper untuk whileloop yang harus ditambahkan ke jawaban ini.
ToolmakerSteve
48

Anda harus dapat memisahkan string dengan cukup mudah, seperti:

aString.Split(Environment.NewLine.ToCharArray());
nikmd23
sumber
46
Pada sistem non-* nix yang akan terpecah pada karakter yang terpisah dalam string Newline, yaitu karakter CR dan LF. Itu akan menyebabkan string kosong ekstra antara setiap baris.
Guffa
Koreksi saya jika saya salah, tetapi bukankah itu terpecah pada karakter \ dan n?
RCIX
7
@RCIX: Tidak, kode \ r dan \ n mewakili karakter tunggal. String "\ r \ n" adalah dua karakter, bukan empat.
Guffa
10
jika Anda menambahkan parameter StringSplitOptions.RemoveEmptyEntries, maka ini akan bekerja dengan sempurna.
Ruben
18
@ Ruben: Tidak, tidak akan. Serge sudah menyarankan itu dalam jawabannya, dan saya sudah menjelaskan bahwa itu juga akan menghapus baris kosong dalam teks asli yang harus dipertahankan.
Guffa
34

Cobalah untuk menghindari menggunakan string. Letakkan untuk solusi umum, karena Anda akan menggunakan lebih banyak memori di mana pun Anda menggunakan fungsi - string asli, dan salinan split, keduanya dalam memori. Percayalah bahwa ini bisa menjadi masalah besar ketika Anda mulai skala - menjalankan aplikasi pemrosesan batch 32-bit memproses 100MB dokumen, dan Anda akan menemukan delapan thread bersamaan. Bukannya aku pernah ke sana sebelumnya ...

Sebaliknya, gunakan iterator seperti ini;

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

Ini akan memungkinkan Anda untuk melakukan loop yang lebih efisien dalam memori di sekitar data Anda;

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

Tentu saja, jika Anda ingin semuanya dalam memori, Anda dapat melakukan ini;

var allTheLines = document.SplitToLines.ToArray();
Steve Cooper
sumber
Saya pernah ke sana ... (parsing file HTML besar dan kehabisan memori). Ya, hindari string. Letakkan. Menggunakan string. Plit dapat mengakibatkan penggunaan Large Object Heap (LOH) - tapi saya tidak 100% yakin akan hal itu.
Peter Mortensen
Jika Anda membuat SplitToLines metode statis (yang menurut Anda dd), lalu bagaimana Anda bisa melakukan blah.SplitToLines.. mis document.SplitToLines...?
barlop
ah saya melihat Anda memasukkan thisparameter formal menjadikannya metode ekstensi.
barlop
26

Berdasarkan jawaban Guffa, di kelas ekstensi, gunakan:

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

Untuk variabel string s:

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

Ini menggunakan definisi akhir garis dari lingkungan Anda. Pada Windows, akhir baris adalah CR-LF (carriage return, line feed) atau dalam karakter pelarian C #\r\n .

Ini adalah solusi yang dapat diandalkan, karena jika Anda menggabungkan kembali garis dengan String.Join, ini sama dengan string asli Anda:

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

Apa yang tidak dilakukan:

  • Menggunakan StringSplitOptions.RemoveEmptyEntries , karena ini akan memecah markup seperti penurunan harga di mana baris kosong memiliki tujuan sintaksis.
  • Split on separator new char[]{Environment.NewLine}, karena pada Windows ini akan membuat satu elemen string kosong untuk setiap baris baru.
Kolonel Panic
sumber
Pada dasarnya jawaban yang sama di sini sebagai yang terbaik, diterima, tetapi memiliki tes unit yang bagus dan peringatan.
vapcguy
8

Regex juga merupakan opsi:

    private string[] SplitStringByLineFeed(string inpString)
    {
        string[] locResult = Regex.Split(inpString, "[\r\n]+");
        return locResult;
    }
pengguna1964822
sumber
7
Jika Anda ingin mencocokkan baris persis, melestarikan baris kosong, string yang regex ini akan lebih baik: "\r?\n".
Rory O'Kane
7

Saya hanya berpikir saya akan menambahkan dua-bit saya, karena solusi lain pada pertanyaan ini tidak termasuk dalam klasifikasi kode yang dapat digunakan kembali dan tidak nyaman.

Blok kode berikut memperluas stringobjek sehingga tersedia sebagai metode alami ketika bekerja dengan string.

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

Anda sekarang dapat menggunakan .Split()fungsi dari string apa pun sebagai berikut:

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

Untuk membagi pada karakter baris baru, cukup lewati "\n"atau "\r\n"sebagai parameter pembatas.

Komentar: Alangkah baiknya jika Microsoft menerapkan kelebihan ini.

Kraang Prime
sumber
Environment.Newlinelebih disukai daripada hard-coding baik \natau \r\n.
Michael Blackburn
3
@MichaelBlackburn - Itu adalah pernyataan yang tidak valid karena tidak ada konteks. Environment.Newlineadalah untuk kompatibilitas lintas platform, bukan untuk bekerja dengan file menggunakan penghentian baris yang berbeda dari sistem operasi saat ini. Lihat di sini untuk informasi lebih lanjut , jadi itu sangat tergantung pada apa yang dikerjakan pengembang. Penggunaan Environment.Newlinememastikan tidak ada konsistensi dalam jenis garis kembali antara OS, di mana 'hard-coding' memberikan kontrol penuh pengembang.
Kraang Prime
2
@MichaelBlackburn - Tidak perlu bagi Anda untuk bersikap kasar. Saya hanya memberikan informasi. .Newlinebukan sihir, di bawah tenda itu hanya string seperti yang disediakan di atas berdasarkan saklar jika itu berjalan di unix, atau di windows. Taruhan paling aman, adalah pertama-tama melakukan pergantian string untuk semua "\ r \ n" dan kemudian berpisah pada "\ n". Di mana penggunaan .Newlinegagal, adalah ketika Anda bekerja dengan file yang disimpan oleh program lain yang menggunakan metode berbeda untuk jeda baris. Ini bekerja dengan baik jika Anda tahu setiap kali file dibaca selalu menggunakan jeda baris OS Anda saat ini.
Kraang Prime
Jadi yang saya dengar adalah cara yang paling mudah dibaca (mungkin penggunaan memori yang lebih tinggi) foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');. Apakah saya mengerti dengan benar bahwa ini berfungsi pada semua platform?
John Doe
4

Saat ini saya menggunakan fungsi ini (berdasarkan jawaban lain) di VB.NET:

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

Itu mencoba untuk membagi pada platform-lokal baris pertama terlebih dahulu, dan kemudian jatuh kembali ke setiap baris yang mungkin baru.

Saya hanya membutuhkan ini di dalam satu kelas sejauh ini. Jika itu berubah, saya mungkin akan membuat iniPublic dan memindahkannya ke kelas utilitas, dan mungkin bahkan membuatnya menjadi metode ekstensi.

Berikut cara menggabungkan garis cadangan, untuk ukuran yang baik:

Private Shared Function JoinLines(lines As IEnumerable(Of String)) As String
    Return String.Join(Environment.NewLine, lines)
End Function
Rory O'Kane
sumber
@Samuel - perhatikan kutipannya. Mereka benar-benar memiliki makna itu. "\r"= kembali. "\r\n"= return + baris baru. (tinjau pos ini dan solusi yang diterima di sini
Kraang Prime
@Kraang Hmm .. Saya belum pernah bekerja dengan .NET dalam waktu yang lama. Saya akan terkejut jika banyak orang yang memilih jawaban yang salah. Saya melihat bahwa saya mengomentari jawaban Guffa juga, dan mendapat klarifikasi di sana. Saya telah menghapus komentar saya untuk jawaban ini. Terimakasih atas peringatannya.
Samuel
2

Nah, sebenarnya split harus dilakukan:

//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]);
}
MaciekTalaska
sumber
2
Opsi RemoveEmptyEntries akan menghapus baris kosong dari teks. Itu mungkin diinginkan dalam beberapa situasi, tetapi perpecahan biasa harus mempertahankan garis kosong.
Guffa
ya, Anda benar, saya baru saja membuat asumsi ini, bahwa ... yah, garis kosong tidak menarik;)
MaciekTalaska
1
string[] lines = text.Split(
  Environment.NewLine.ToCharArray(), 
  StringSplitOptions.RemoveEmptyStrings);

The RemoveEmptyStrings pilihan akan memastikan Anda tidak memiliki entri kosong karena \ n menyusul \ r

(Edit untuk mencerminkan komentar :) Perhatikan bahwa itu juga akan membuang baris kosong asli dalam teks. Ini biasanya yang saya inginkan tetapi mungkin bukan kebutuhan Anda.

Serge Wautier
sumber
Opsi RemoveEmptyStrings juga akan menghapus baris kosong, sehingga tidak berfungsi dengan baik jika teks memiliki baris kosong di dalamnya.
Guffa
Anda mungkin ingin mempertahankan garis kosong asli: \ r \ n \ r \ n
slim
0

Saya tidak tahu tentang Lingkungan. Garis Baru, tapi saya kira ini adalah solusi yang sangat bagus.

Usaha saya adalah:

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

.Trim tambahan menghapus semua \ r atau \ n yang mungkin masih ada (misalnya ketika di windows tetapi memisahkan string dengan os x karakter baris baru). Mungkin bukan metode tercepat.

EDIT:

Seperti yang ditunjukkan oleh komentar dengan benar, ini juga menghilangkan spasi putih di awal baris atau sebelum umpan baris baru. Jika Anda perlu mempertahankan spasi putih itu, gunakan salah satu opsi lain.

Maks
sumber
Trim juga akan menghilangkan spasi putih di awal dan akhir garis, misalnya lekukan.
Guffa
".Trim menghapus \ r atau \ n yang mungkin masih ada" - aduh. Mengapa tidak menulis kode yang kuat saja?
bzlm
Mungkin saya salah menjawab, tetapi tidak jelas bahwa spasi putih harus dilestarikan. Tentu saja Anda benar, Trim () juga menghapus spasi putih.
Maks
1
@ Max: Wow, tunggu sampai saya memberi tahu bos saya bahwa kode diizinkan untuk melakukan apa pun yang tidak secara khusus dikesampingkan dalam spesifikasi ...;)
Guffa
-2

Jawaban konyol: menulis ke file sementara sehingga Anda dapat menggunakan yang terhormat 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);
Kolonel Panic
sumber
1
Hindari var, karena tidak menentukan jenis variabel, jadi Anda mungkin tidak mengerti bagaimana menggunakan objek itu, atau apa yang diwakili objek itu. Plus, ini menunjukkan penulisan baris dan bahkan tidak menentukan nama file, jadi saya ragu itu akan berhasil. Kemudian, saat membaca, jalur ke file sekali lagi tidak ditentukan. Dengan asumsi bahwa pathadalah C:\Temp\test.txt, Anda harus kemudian memiliki string[] lines = File.ReadLines(path);.
vapcguy
1
@vapcguy apa yang baru saja saya baca? - Saya akan merekomendasikan untuk membaca kembali posting atau debug di program konsol karena semua yang Anda katakan adalah salah | | path diatur pada Path.GetTempFileName | var adalah definisi yang umum dan direkomendasikan dalam C # - dengan cara ia mendefinisikan jenis variabel ...... EDIT: Saya tidak mengatakan ini adalah solusi yang baik
koanbock
@ Koanbock Ok, jadi saya mencari Path.GetTempFileName msdn.microsoft.com/en-us/library/… dan mengatakan ia membuat file nol-byte & mengembalikan "path lengkap file itu". Saya bersumpah telah mencoba ini sebelumnya dan memberikan pengecualian karena tidak menemukan file, tetapi dikembalikan ke lokasi folder. Saya tahu argumen untuk menggunakan var, tetapi saya akan mengatakan itu TIDAK direkomendasikan karena tidak menunjukkan apa objek variabel. Itu mengaburkannya.
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);
        }
    }
}
maciej
sumber
-5

Sangat mudah sebenarnya.

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);
}
Skillaura13
sumber
4
Benar-benar salah dan tidak berfungsi. Plus, dalam C #, Environment.NewLinesama seperti di VB.
vapcguy
Lihat Pengidentifikasi end-of-line di VB.NET? untuk opsi berbeda untuk jalur baru.
Peter Mortensen