คุณจะแยกสตริงหลายบรรทัดออกเป็นบรรทัดได้อย่างไร?
ฉันรู้อย่างนี้
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
ดูน่าเกลียดเล็กน้อยและสูญเสียบรรทัดว่าง มีวิธีแก้ไขที่ดีกว่านี้หรือไม่?
คุณจะแยกสตริงหลายบรรทัดออกเป็นบรรทัดได้อย่างไร?
ฉันรู้อย่างนี้
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
ดูน่าเกลียดเล็กน้อยและสูญเสียบรรทัดว่าง มีวิธีแก้ไขที่ดีกว่านี้หรือไม่?
ถ้ามันดูน่าเกลียด เพียงลบการเรียก ToCharArray
ที่ไม่จำเป็นออก
หากคุณต้องการแยกด้วย \n
หรือ \r
คุณมีสองตัวเลือก:
ใช้อาร์เรย์ตามตัวอักษร – แต่จะทำให้มีบรรทัดว่างสำหรับการลงท้ายบรรทัดแบบ Windows \r\n
:
var result = text.Split(new [] { '\r', '\n' });
ใช้นิพจน์ทั่วไป ตามที่ Bart ระบุ:
var result = Regex.Split(text, "\r\n|\r|\n");
หากคุณต้องการเก็บบรรทัดว่าง ทำไมคุณถึงบอก C# ให้ทิ้งมันไปอย่างชัดเจน? (พารามิเตอร์ StringSplitOptions
) – ใช้ StringSplitOptions.None
แทน
Environment.NewLine
จึงไม่ต้องไปไกลเท่าที่ฉันกังวล อันที่จริงแล้ว ในบรรดาโซลูชันที่เป็นไปได้ทั้งหมด ฉันชอบแบบที่ใช้นิพจน์ทั่วไปมากกว่า เนื่องจากมีเพียงโซลูชันเดียวที่จัดการแพลตฟอร์มต้นทางทั้งหมดได้อย่างถูกต้อง
- person Konrad Rudolph; 20.01.2011
StringSplitOptions.RemoveEmptyEntries
- person Konrad Rudolph; 19.10.2011
Regex.Split
นั่นคือ - var result = Regex.Split(text, @"\r\n|\r|\n");
ในกรณีนี้มันทำงานด้วยวิธีใดวิธีหนึ่งเนื่องจากคอมไพเลอร์ C# ตีความ \n และ \r ในลักษณะเดียวกับที่ตัวแยกวิเคราะห์นิพจน์ทั่วไปทำ ในกรณีทั่วไปถึงแม้จะอาจทำให้เกิดปัญหาก็ตาม
- person Ken Clement; 16.11.2017
"\r"
เพียงอย่างเดียวเป็นตัวคั่นบรรทัด (เพราะว่า ยอมรับเถอะ ไม่มีระบบใดใช้แบบแผนนี้มานานกว่าทศวรรษ): แบบแผนที่ใช้จริงเพียงแบบเดียวคือ "\r\n"
และ "\n"
อันที่จริง ตัวอย่างของคุณ ("\n\r"
) ไม่เคย มีการขึ้นบรรทัดใหม่ที่ถูกต้องเลย อ่านว่ามีการขึ้นบรรทัดใหม่ สอง หรือมีข้อผิดพลาดเกิดขึ้น แต่อย่าถือเป็นการขึ้นบรรทัดเดียวอย่างแน่นอน
- person Konrad Rudolph; 21.08.2018
"\r"
ไม่เคยใช้เป็นตัวคั่น ดังนั้นคุณจึงสามารถเขียนตัวแยกวิเคราะห์สากลที่ยอมรับตัวคั่นที่ใช้จริงทั้งหมดได้อย่างง่ายดาย ทำได้โดยเพียงแค่แยกบน "\r\n|\n"
ไม่จำเป็นต้องมีอะไรแฟนซีไปกว่านี้อีกแล้ว แต่ตามจริงแล้ว ในทางปฏิบัติไม่มีอะไรผิดปกติกับโค้ด regex ที่แสดงในคำตอบของฉัน และมันจะทำงานได้ดีกับไฟล์ที่ผสมผสานสไตล์การขึ้นบรรทัดใหม่ที่แตกต่างกัน รวมถึง "\r"
ที่ล้าสมัยด้วย
- person Konrad Rudolph; 21.08.2018
diff
ตั้งค่าสถานะไฟล์เหล่านั้น) นี่ไม่ใช่ "การวางแผนสำหรับอนาคต" แต่เป็นการสร้างโค้ดที่แข็งแกร่งสำหรับที่นี่และเดี๋ยวนี้
- person Konrad Rudolph; 21.08.2018
มันใช้งานได้ดีและเร็วกว่า Regex:
input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None)
สิ่งสำคัญคือต้องมี "\r\n"
อยู่ในอาร์เรย์ก่อน เพื่อให้ถือเป็นตัวแบ่งบรรทัดเดียว ข้อมูลข้างต้นให้ผลลัพธ์เดียวกันกับโซลูชัน Regex อย่างใดอย่างหนึ่งเหล่านี้:
Regex.Split(input, "\r\n|\r|\n")
Regex.Split(input, "\r?\n|\r")
ยกเว้นว่า Regex จะช้ากว่าประมาณ 10 เท่า นี่คือการทดสอบของฉัน:
Action<Action> measure = (Action func) => {
var start = DateTime.Now;
for (int i = 0; i < 100000; i++) {
func();
}
var duration = DateTime.Now - start;
Console.WriteLine(duration);
};
var input = "";
for (int i = 0; i < 100; i++)
{
input += "1 \r2\r\n3\n4\n\r5 \r\n\r\n 6\r7\r 8\r\n";
}
measure(() =>
input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None)
);
measure(() =>
Regex.Split(input, "\r\n|\r|\n")
);
measure(() =>
Regex.Split(input, "\r?\n|\r")
);
เอาต์พุต:
00:00:03.8527616
00:00:31.8017726
00:00:32.5557128
และนี่คือ วิธีการขยาย:
public static class StringExtensionMethods
{
public static IEnumerable<string> GetLines(this string str, bool removeEmptyLines = false)
{
return str.Split(new[] { "\r\n", "\r", "\n" },
removeEmptyLines ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None);
}
}
การใช้งาน:
input.GetLines() // keeps empty lines
input.GetLines(true) // removes empty lines
[\r\n]{1,2}
- person ΩmegaMan; 27.02.2015
\n\r
หรือ \n\n
เป็นการขึ้นบรรทัดใหม่ซึ่งไม่ถูกต้อง
- person orad; 28.02.2015
Hello\n\nworld\n\n
เป็นเคสขอบได้อย่างไร? เห็นได้ชัดว่าเป็นหนึ่งบรรทัดพร้อมข้อความ ตามด้วยบรรทัดว่าง ตามด้วยอีกบรรทัดพร้อมข้อความ ตามด้วยบรรทัดว่าง
- person Brandin; 09.08.2015
คุณสามารถใช้ Regex.Split:
string[] tokens = Regex.Split(input, @"\r?\n|\r");
แก้ไข: เพิ่ม |\r
ในบัญชีสำหรับตัวยุติบรรทัด Mac (เก่ากว่า)
\r
เป็นจุดสิ้นสุดบรรทัด
- person Konrad Rudolph; 02.10.2009
[\r\n]{1,2}
- person ΩmegaMan; 27.02.2015
หากคุณต้องการเก็บบรรทัดว่างไว้ ให้ลบ StringSplitOptions ออก
var result = input.Split(System.Environment.NewLine.ToCharArray());
ฉันมีคำตอบอื่นนี้ แต่คำตอบนี้อ้างอิงจากตอบ เร็วกว่ามาก เนื่องจากทำงานแบบอะซิงโครนัส แม้ว่าจะช้ากว่าเล็กน้อยก็ตาม
public static class StringExtensionMethods
{
public static IEnumerable<string> GetLines(this string str, bool removeEmptyLines = false)
{
using (var sr = new StringReader(str))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (removeEmptyLines && String.IsNullOrWhiteSpace(line))
{
continue;
}
yield return line;
}
}
}
}
การใช้งาน:
input.GetLines() // keeps empty lines
input.GetLines(true) // removes empty lines
ทดสอบ:
Action<Action> measure = (Action func) =>
{
var start = DateTime.Now;
for (int i = 0; i < 100000; i++)
{
func();
}
var duration = DateTime.Now - start;
Console.WriteLine(duration);
};
var input = "";
for (int i = 0; i < 100; i++)
{
input += "1 \r2\r\n3\n4\n\r5 \r\n\r\n 6\r7\r 8\r\n";
}
measure(() =>
input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)
);
measure(() =>
input.GetLines()
);
measure(() =>
input.GetLines().ToList()
);
เอาต์พุต:
00:00:03.9603894
00:00:00.0029996
00:00:04.8221971
บิดเบี้ยวเล็กน้อย แต่เป็นบล็อกตัววนซ้ำที่ต้องทำ:
public static IEnumerable<string> Lines(this string Text)
{
int cIndex = 0;
int nIndex;
while ((nIndex = Text.IndexOf(Environment.NewLine, cIndex + 1)) != -1)
{
int sIndex = (cIndex == 0 ? 0 : cIndex + 1);
yield return Text.Substring(sIndex, nIndex - sIndex);
cIndex = nIndex;
}
yield return Text.Substring(cIndex + 1);
}
จากนั้นคุณสามารถโทร:
var result = input.Lines().ToArray();
เป็นเรื่องยากที่จะจัดการกับการลงท้ายบรรทัด ผสม อย่างถูกต้อง ดังที่เราทราบ อักขระการสิ้นสุดบรรทัดอาจเป็น "Line Feed" (ASCII 10, \n
, \x0A
, \u000A
), "Carriage Return" (ASCII 13, \r
, \x0D
, \u000D
) หรือบางส่วนผสมกัน ย้อนกลับไปที่ DOS Windows จะใช้ลำดับอักขระสองตัว CR-LF \u000D\u000A
ดังนั้นชุดค่าผสมนี้ควรปล่อยบรรทัดเดียวเท่านั้น Unix ใช้ \u000A
ตัวเดียว ส่วน Mac รุ่นเก่าๆ ใช้อักขระ \u000D
ตัวเดียว วิธีมาตรฐานในการจัดการกับการผสมผสานของอักขระเหล่านี้ภายในไฟล์ข้อความเดียวมีดังนี้:
\u000D\u000A
) ทันที ทั้งสองร่วมกันจะข้ามเพียงบรรทัดเดียวString.Empty
เป็นอินพุตเดียวที่ไม่ส่งคืนบรรทัด (อักขระใดๆ มีอย่างน้อยหนึ่งบรรทัด)กฎก่อนหน้านี้อธิบายลักษณะการทำงานของ StringReader.ReadLine และฟังก์ชันที่เกี่ยวข้อง และฟังก์ชันที่แสดงด้านล่างนี้ให้ผลลัพธ์ที่เหมือนกัน เป็นฟังก์ชันการแบ่งบรรทัด C# ที่มีประสิทธิภาพ ซึ่งนำหลักเกณฑ์เหล่านี้ไปใช้อย่างถูกต้องเพื่อจัดการกับลำดับที่กำหนดเองหรือการรวมกันของ CR/LF อย่างถูกต้อง บรรทัดที่แจงนับไม่มีอักขระ CR/LF ใดๆ บรรทัดว่างจะถูกรักษาและส่งกลับเป็น String.Empty
/// <summary>
/// Enumerates the text lines from the string.
/// ⁃ Mixed CR-LF scenarios are handled correctly
/// ⁃ String.Empty is returned for each empty line
/// ⁃ No returned string ever contains CR or LF
/// </summary>
public static IEnumerable<String> Lines(this String s)
{
int j = 0, c, i;
char ch;
if ((c = s.Length) > 0)
do
{
for (i = j; (ch = s[j]) != '\r' && ch != '\n' && ++j < c;)
;
yield return s.Substring(i, j - i);
}
while (++j < c && (ch != '\r' || s[j] != '\n' || ++j < c));
}
หมายเหตุ: หากคุณไม่สนใจค่าใช้จ่ายในการสร้างอินสแตนซ์ StringReader
ในการเรียกแต่ละครั้ง คุณสามารถใช้โค้ด C# 7 ต่อไปนี้แทนได้ ตามที่ระบุไว้ แม้ว่าตัวอย่างข้างต้นอาจมีประสิทธิภาพมากกว่าเล็กน้อย แต่ฟังก์ชันทั้งสองนี้ให้ผลลัพธ์ที่เหมือนกันทุกประการ
public static IEnumerable<String> Lines(this String s)
{
using (var tr = new StringReader(s))
while (tr.ReadLine() is String L)
yield return L;
}
แยกสตริงออกเป็นบรรทัดโดยไม่มีการจัดสรรใดๆ
public static LineEnumerator GetLines(this string text) {
return new LineEnumerator( text.AsSpan() );
}
internal ref struct LineEnumerator {
private ReadOnlySpan<char> Text { get; set; }
public ReadOnlySpan<char> Current { get; private set; }
public LineEnumerator(ReadOnlySpan<char> text) {
Text = text;
Current = default;
}
public LineEnumerator GetEnumerator() {
return this;
}
public bool MoveNext() {
if (Text.IsEmpty) return false;
var index = Text.IndexOf( '\n' ); // \r\n or \n
if (index != -1) {
Current = Text.Slice( 0, index + 1 );
Text = Text.Slice( index + 1 );
return true;
} else {
Current = Text;
Text = ReadOnlySpan<char>.Empty;
return true;
}
}
}
IEnumerable<>
หรือไม่?
- person Konstantin Spirin; 01.02.2021
string.Split
หรือ Regex.Split
)
- person Uwe Keim; 25.01.2019