วิธีที่ง่ายที่สุดในการแบ่งสตริงบนบรรทัดใหม่ใน. NET?


806

ฉันต้องแยกสตริงออกเป็นบรรทัดใหม่ใน. NET และวิธีเดียวที่ฉันรู้ว่าจะแบ่งสตริงคือด้วยวิธีการแยก อย่างไรก็ตามนั่นจะไม่อนุญาตให้ฉันแยก (ขึ้นบรรทัดใหม่) ได้อย่างง่ายดายดังนั้นวิธีที่ดีที่สุดในการทำคืออะไร


2
ทำไมถึงไม่ได้? เพียงแยกบน System.En
Environment.NewLine

16
แต่คุณต้องห่อมันไว้ในสตริง [] และเพิ่มอาร์กิวเมนต์พิเศษและ ... มันแค่รู้สึก clunky
RCIX

คำตอบ:


1413

ในการแยกสตริงคุณจำเป็นต้องใช้โอเวอร์โหลดที่ใช้อาร์เรย์ของสตริง:

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

แก้ไข:
หากคุณต้องการจัดการตัวแบ่งบรรทัดประเภทต่าง ๆ ในข้อความคุณสามารถใช้ความสามารถในการจับคู่มากกว่าหนึ่งสตริง สิ่งนี้จะถูกแบ่งอย่างถูกต้องในการแบ่งบรรทัดทั้งสองประเภทและเก็บรักษาบรรทัดว่างและระยะห่างในข้อความ:

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

3
@RCIX: การส่งพารามิเตอร์ที่ถูกต้องไปยังเมธอดนั้นค่อนข้างอึดอัดเพราะคุณใช้มันสำหรับบางสิ่งที่ง่ายกว่ามาก ๆ อย่างน้อยก็มีก่อนที่จะมีกรอบ 2 คุณมีการใช้การแสดงออกปกติหรือสร้างกิจวัตรประจำวันแยกของคุณเองเพื่อแยกสตริง ...
Guffa

4
@Leandro: Environment.NewLineคุณสมบัติมีการขึ้นบรรทัดใหม่เริ่มต้นสำหรับระบบ สำหรับระบบปฏิบัติการ Windows "\r\n"ตัวอย่างเช่นมันจะเป็น
Guffa

3
@ Leandro: หนึ่งเดาว่าโปรแกรมจะแยก\nออกจาก\rที่ปลายแต่ละบรรทัดแล้วส่งออกบรรทัดที่มี\r\nระหว่างพวกเขา
Guffa

3
@Samuel: The \rและ\nescape sequences (ในหมู่อื่น ๆ ) มีความหมายพิเศษกับคอมไพเลอร์ C # VB ไม่มีลำดับการหลีกเลี่ยงเหล่านั้นดังนั้นจึงใช้ค่าคงที่เหล่านั้นแทน
Guffa

2
หากคุณต้องการยอมรับไฟล์จากระบบปฏิบัติการต่าง ๆ มากมายคุณอาจเพิ่ม "\ n \ r" ลงในจุดเริ่มต้นและ "\ r" ที่ส่วนท้ายของรายการตัวคั่น ฉันไม่แน่ใจว่ามันคุ้มค่ากับประสิทธิภาพที่ได้รับ ( en.wikipedia.org/wiki/Newline )
user420667

121

สิ่งที่เกี่ยวกับการใช้StringReader?

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

13
นี่คือสิ่งที่ฉันชอบ ฉันห่อด้วยวิธีการขยายและให้ผลตอบแทนบรรทัดปัจจุบัน: gist.github.com/ronnieoverby/7916886
Ronnie Overby

3
นี่เป็นโซลูชันที่ไม่ใช่ regex เดียวที่ฉันพบสำหรับ. netcf 3.5
Carl

8
ดีเป็นพิเศษเมื่ออินพุตมีขนาดใหญ่และการคัดลอกไปยังอาเรย์จะช้า / ใช้หน่วยความจำมาก
Alejandro

1
ตามที่เขียนไว้คำตอบนี้จะอ่านบรรทัดแรกเท่านั้น ดูคำตอบของ Steve Cooper ในการwhileวนซ้ำที่ควรเพิ่มในคำตอบนี้
ToolmakerSteve

48

คุณควรจะสามารถแยกสตริงของคุณได้อย่างง่ายดายเช่น:

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

46
บนระบบ non- * ห้ามที่จะแยกอักขระที่แยกต่างหากในสตริง Newline เช่นอักขระ CR และ LF ที่จะทำให้สตริงว่างพิเศษระหว่างแต่ละบรรทัด
Guffa

ถูกต้องฉันถ้าฉันผิด แต่จะไม่แยกที่ตัวอักษร \ และ n?
RCIX

7
@RCIX: ไม่รหัส \ r และ \ n แสดงถึงอักขระเดี่ยว สตริง "\ r \ n" เป็นอักขระสองตัวไม่ใช่สี่ตัว
Guffa

10
ถ้าคุณเพิ่มพารามิเตอร์ StringSplitOptions.RemoveEmptyEntries นี่จะทำงานได้อย่างสมบูรณ์
Ruben

18
@ Ruben: ไม่มันจะไม่ เสิร์จแนะนำแล้วว่าในคำตอบของเขาและฉันได้อธิบายว่า aldready จะลบบรรทัดว่างในข้อความต้นฉบับที่ควรได้รับการเก็บรักษาไว้
Guffa

34

พยายามหลีกเลี่ยงการใช้สตริงแยกสำหรับโซลูชันทั่วไปเนื่องจากคุณจะใช้หน่วยความจำมากขึ้นทุกที่ที่คุณใช้ฟังก์ชั่น - สตริงเดิมและสำเนาแยกทั้งในหน่วยความจำ เชื่อฉันเถอะว่านี่อาจเป็นปัญหาหนึ่งเมื่อคุณเริ่มขยายขนาด - เรียกใช้แอปประมวลผลแบบ 32 บิตที่ประมวลผลเอกสาร 100MB และคุณจะแยกแยะหัวข้อแปดเธรดที่เกิดขึ้นพร้อมกัน ไม่ใช่ว่าฉันเคยไปที่นั่นมาก่อน ...

ให้ใช้ตัววนซ้ำแบบนี้แทน

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

วิธีนี้จะช่วยให้คุณสามารถวนรอบหน่วยความจำที่มีประสิทธิภาพมากขึ้น

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

แน่นอนถ้าคุณต้องการมันทั้งหมดในความทรงจำคุณสามารถทำได้

var allTheLines = document.SplitToLines.ToArray();

ฉันไปที่นั่นแล้ว ... (แยกไฟล์ HTML ขนาดใหญ่และหน่วยความจำไม่เพียงพอ) ใช่หลีกเลี่ยงสตริงแยกออก การใช้สตริงแยกอาจส่งผลให้การใช้วัตถุขนาดใหญ่กอง (LOH) - แต่ฉันไม่แน่ใจ 100%
Peter Mortensen

หากคุณทำ SplitToLines เป็นวิธีการแบบคงที่ (ซึ่งดูเหมือนว่าคุณ dd) แล้วคุณจะทำอย่างไรblah.SplitToLines.. เช่นdocument.SplitToLines...?
barlop

ฉันเห็นว่าคุณใส่thisพารามิเตอร์ทางการทำให้เป็นวิธีการขยาย
barlop


9

สำหรับตัวแปรสตริงs:

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

สิ่งนี้ใช้นิยามของการสิ้นสุดบรรทัดของสภาวะแวดล้อมของคุณ บน Windows, ปลายสาย CR-LF (กลับรถ, อาหารเส้น) หรือใน C # 's \r\nตัวหนี

นี่เป็นวิธีการแก้ปัญหาที่เชื่อถือได้เพราะถ้าคุณรวมสายอีกครั้งด้วยString.Joinนี่จะเท่ากับสตริงเดิมของคุณ:

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

สิ่งที่ไม่ควรทำ:

  • ใช้StringSplitOptions.RemoveEmptyEntriesเพราะจะทำให้มาร์กอัปแตกเช่นมาร์กดาวน์ที่บรรทัดว่างมีวัตถุประสงค์ในการสร้างประโยค
  • แยกบนตัวคั่นnew char[]{Environment.NewLine}เนื่องจากใน Windows จะสร้างองค์ประกอบสตริงว่างหนึ่งรายการสำหรับแต่ละบรรทัดใหม่

โดยทั่วไปคำตอบเดียวกับที่นี่ซึ่งเป็นคำตอบที่ได้รับการยอมรับ แต่มีการทดสอบหน่วยและคำเตือนที่ดี
vapcguy

8

Regex ยังเป็นตัวเลือก:

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

7
ถ้าคุณต้องการเพื่อให้ตรงกับเส้นตรงรักษาบรรทัดว่างนี้สตริง regex "\r?\n"จะดีกว่า:
Rory O'Kane

7

ฉันแค่คิดว่าฉันจะเพิ่มสองบิตของฉันเพราะโซลูชันอื่น ๆ ในคำถามนี้ไม่ได้อยู่ในการจำแนกรหัสที่สามารถนำกลับมาใช้ใหม่ได้และไม่สะดวก

บล็อกของรหัสต่อไปนี้ขยาย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);
        }
    }
}

ตอนนี้คุณสามารถใช้.Split()ฟังก์ชันจากสตริงใด ๆ ดังต่อไปนี้:

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

หากต้องการแยกอักขระขึ้นบรรทัดใหม่ให้ส่ง"\n"หรือผ่าน"\r\n"พารามิเตอร์ตัวคั่น

ความคิดเห็น: มันจะดีถ้า Microsoft ใช้งานเกินนี้


Environment.Newlineเป็นที่ต้องการการเข้ารหัสที่ยากอย่างใดอย่างหนึ่งหรือ\n \r\n
Michael Blackburn

3
@MichaelBlackburn - นั่นเป็นคำสั่งที่ไม่ถูกต้องเพราะไม่มีบริบท Environment.Newlineใช้สำหรับการทำงานร่วมกันข้ามแพลตฟอร์มไม่ใช่สำหรับการทำงานกับไฟล์ที่ใช้การยุติบรรทัดที่แตกต่างจากระบบปฏิบัติการปัจจุบัน ดูข้อมูลเพิ่มเติมได้ที่นี่ดังนั้นขึ้นอยู่กับสิ่งที่นักพัฒนาทำงานด้วย การใช้Environment.Newlineเพื่อให้แน่ใจว่าไม่มีความสอดคล้องในประเภทการส่งคืนบรรทัดระหว่างระบบปฏิบัติการซึ่ง 'การเข้ารหัสแบบยาก' ให้การควบคุมเต็มรูปแบบแก่นักพัฒนา
Kraang Prime

2
@MichaelBlackburn - ไม่จำเป็นต้องให้คุณหยาบคาย ฉันแค่ให้ข้อมูลเท่านั้น .Newlineไม่ใช่เวทย์มนตร์ภายใต้ประทุนมันเป็นเพียงแค่สายอักขระที่ให้ไว้ข้างต้นขึ้นอยู่กับสวิตช์ของถ้ามันทำงานบนยูนิกซ์หรือบนหน้าต่าง เดิมพันที่ปลอดภัยที่สุดคือการทำสตริงก่อนสำหรับ "\ r \ n" ทั้งหมดแล้วแยกใน "\ n" การใช้.Newlineล้มเหลวคือเมื่อคุณทำงานกับไฟล์ที่ถูกบันทึกโดยโปรแกรมอื่นที่ใช้วิธีการอื่นในการแบ่งบรรทัด มันทำงานได้ดีถ้าคุณรู้ทุกครั้งที่อ่านไฟล์จะใช้ตัวแบ่งบรรทัดของระบบปฏิบัติการปัจจุบันของคุณเสมอ
Kraang Prime

ดังนั้นสิ่งที่ฉันได้ยินเป็นวิธีที่สามารถอ่านได้มากที่สุด (ใช้หน่วยความจำอาจจะสูงกว่า) foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');จะ ฉันเข้าใจถูกต้องหรือไม่ว่านี่ใช้ได้กับทุกแพลตฟอร์มหรือไม่
John Doe

4

ขณะนี้ฉันใช้ฟังก์ชันนี้ (ตามคำตอบอื่น ๆ ) ใน VB.NET:

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

มันพยายามที่จะแยกบนบรรทัดใหม่แพลตฟอร์มท้องถิ่นก่อนและจากนั้นกลับไปแต่ละบรรทัดใหม่ที่เป็นไปได้

ฉันต้องการเพียงสิ่งนี้ในชั้นเรียนหนึ่งจนถึง หากการเปลี่ยนแปลงนั้นฉันอาจจะทำสิ่งนี้Publicและย้ายไปที่คลาสยูทิลิตี้และอาจทำให้เป็นวิธีการขยาย

ต่อไปนี้เป็นวิธีเข้าร่วมการสำรองข้อมูลเพื่อการวัดที่ดี:

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

@Samuel - บันทึกใบเสนอราคา จริงๆแล้วพวกเขามีความหมายนั้น "\r"= return "\r\n"= return + new line (โปรดอ่านโพสต์นี้และทางออกที่ได้รับการยอมรับที่นี่
Kraang Prime

@ กระแตอืม .. ฉันไม่ได้ทำงานกับ. NET มานานแล้ว ฉันจะแปลกใจถ้าคนจำนวนมากโหวตให้คำตอบที่ผิด ฉันเห็นว่าฉันแสดงความคิดเห็นต่อคำตอบของ Guffa ด้วยและได้รับความกระจ่าง ฉันลบความคิดเห็นของฉันไปยังคำตอบนี้แล้ว ขอบคุณสำหรับหัวขึ้น.
ซามูเอล

2

ดีจริง ๆ แล้วควรแยก:

//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
ตัวเลือก RemoveEmptyEntries จะลบบรรทัดว่างออกจากข้อความ อาจเป็นที่ต้องการในบางสถานการณ์ แต่การแบ่งแบบธรรมดาควรรักษาบรรทัดว่างไว้
Guffa

ใช่คุณพูดถูกฉันเพิ่งตั้งสมมติฐานว่า ...
เอาละ

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

RemoveEmptyStringsตัวเลือกที่จะทำให้แน่ใจว่าคุณไม่ได้มีรายการที่ว่างเปล่าเนื่องจาก \ n ต่อไปนี้ \ r

(แก้ไขเพื่อสะท้อนความคิดเห็น :) โปรดทราบว่าจะเป็นการลบบรรทัดว่างเปล่าในข้อความด้วย นี่คือสิ่งที่ฉันต้องการ แต่มันอาจไม่ใช่ความต้องการของคุณ


ตัวเลือก RemoveEmptyStrings จะลบบรรทัดว่างดังนั้นจึงใช้งานไม่ได้หากข้อความมีบรรทัดว่างอยู่
Guffa

คุณอาจต้องการรักษาบรรทัดว่างเปล่าของแท้: \ r \ n \ r \ n
บาง

0

ฉันไม่ทราบเกี่ยวกับสิ่งแวดล้อมบรรทัดใหม่ แต่ฉันเดาว่านี่เป็นวิธีแก้ปัญหาที่ดีมาก

ฉันจะลอง:

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

.Trim เพิ่มเติมจะลบ \ r หรือ \ n ใด ๆ ที่อาจยังคงปรากฏอยู่ (เช่นเมื่ออยู่บน windows แต่การแยกสตริงด้วยอักขระ os x newline) อาจไม่ใช่วิธีที่เร็วที่สุด

แก้ไข:

เมื่อความคิดเห็นชี้ไปอย่างถูกต้องสิ่งนี้จะลบช่องว่างใด ๆ ที่จุดเริ่มต้นของบรรทัดหรือก่อนป้อนบรรทัดใหม่ หากคุณต้องการรักษาพื้นที่ว่างนั้นใช้หนึ่งในตัวเลือกอื่น ๆ


การตัดแต่งจะลบพื้นที่สีขาวที่จุดเริ่มต้นและจุดสิ้นสุดของบรรทัดเช่นการเยื้อง
Guffa

".Trim ลบ \ r หรือ \ n ใด ๆ ที่อาจยังคงปรากฏอยู่" - ouch ทำไมไม่เขียนโค้ดที่มีประสิทธิภาพแทน?
bzlm

บางทีฉันอาจมีคำถามผิด แต่มัน / ไม่ชัดเจนว่าช่องว่างนั้นต้องได้รับการเก็บรักษาไว้ แน่นอนว่าคุณพูดถูก Trim () จะลบช่องว่างออกด้วย
สูงสุด

1
@Max: ว้าวรอจนกว่าเราจะบอกเจ้านายของฉันว่ารหัสที่ได้รับอนุญาตที่จะทำอะไรที่ไม่ได้ปกครองโดยเฉพาะออกมาในสเปค ... ;)
Guffa

-2

คำตอบ Silly: เขียนไปที่ไฟล์ชั่วคราวเพื่อให้คุณสามารถใช้เป็นที่เคารพ 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
หลีกเลี่ยงvarเนื่องจากมันไม่ได้กำหนดประเภทของตัวแปรดังนั้นคุณอาจไม่เข้าใจวิธีการใช้วัตถุนั้นหรือวัตถุนั้นหมายถึงอะไร นอกจากนี้ยังแสดงการเขียนบรรทัดและไม่ได้ระบุชื่อไฟล์ด้วยดังนั้นฉันสงสัยว่ามันจะใช้งานได้ จากนั้นเมื่ออ่านเส้นทางไม่ได้ระบุพา ธ ไปยังไฟล์อีกครั้ง สมมติว่าpathเป็นแล้วคุณควรจะมีC:\Temp\test.txt string[] lines = File.ReadLines(path);
vapcguy

1
@vccguy ฉันเพิ่งอ่านอะไร - ฉันอยากจะแนะนำให้อ่านโพสต์ใหม่หรือแก้ไขข้อบกพร่องในโปรแกรมคอนโซลเพราะสิ่งที่คุณบอกว่าผิดธรรมดา | เส้นทางตั้งอยู่บน Path.GetTempFileName | var เป็นคำจำกัดความทั่วไปที่แนะนำใน C # - โดยวิธีการที่จะกำหนดประเภทของตัวแปร ...... แก้ไข: ฉันไม่ได้พูดว่านี่เป็นทางออกที่ดี
koanbock

@koanbock ตกลงดังนั้นฉันจึงPath.GetTempFileName ค้นหา msdn.microsoft.com/en-us/library/ ......และมันบอกว่ามันสร้างไฟล์เป็นศูนย์ไบต์และส่งกลับ "เส้นทางแบบเต็มของไฟล์นั้น" ฉันสาบานได้ว่าฉันได้ลองทำสิ่งนี้มาก่อนและมันมีข้อยกเว้นเพราะหาไฟล์ไม่พบ แต่ได้ส่งคืนตำแหน่งโฟลเดอร์แทน ฉันรู้ว่าข้อโต้แย้งในการใช้varแต่ฉันบอกว่าไม่แนะนำเพราะไม่แสดงว่าวัตถุตัวแปร มันทำให้งงงวยมัน
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

ง่ายมากจริง ๆ

VB.NET:

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

ค#:

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

4
ไม่ถูกต้องทั้งหมดและใช้งานไม่ได้ นอกจากนี้ใน C # ก็Environment.NewLineเหมือนกับใน VB
vapcguy

ดูตัวระบุปลายทางใน VB.NET หรือไม่ สำหรับตัวเลือกต่าง ๆ สำหรับบรรทัดใหม่
Peter Mortensen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.