आप मल्टी-लाइन स्ट्रिंग को लाइनों में कैसे विभाजित करते हैं?
मैं इस तरह से जानता हूं
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
थोड़ा बदसूरत दिखता है और खाली लाइनें खो देता है। क्या कोई बेहतर समाधान है?
आप मल्टी-लाइन स्ट्रिंग को लाइनों में कैसे विभाजित करते हैं?
मैं इस तरह से जानता हूं
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
थोड़ा बदसूरत दिखता है और खाली लाइनें खो देता है। क्या कोई बेहतर समाधान है?
जवाबों:
यदि यह बदसूरत दिखता है, तो अनावश्यक ToCharArrayकॉल को हटा दें ।
यदि आप \nया तो विभाजित करना चाहते हैं या \r, आपके पास दो विकल्प हैं:
एक सरणी शाब्दिक का उपयोग करें - लेकिन इससे आपको विंडोज-स्टाइल लाइन अंत के लिए खाली लाइनें मिलेंगी \r\n:
var result = text.Split(new [] { '\r', '\n' });एक नियमित अभिव्यक्ति का उपयोग करें, जैसा कि बार्ट द्वारा इंगित किया गया है:
var result = Regex.Split(text, "\r\n|\r|\n");यदि आप खाली लाइनों को संरक्षित करना चाहते हैं, तो आप उन्हें फेंकने के लिए C # को स्पष्ट रूप से क्यों बताते हैं? ( StringSplitOptionsपैरामीटर) - StringSplitOptions.Noneइसके बजाय उपयोग करें ।
Environment.NewLineएक नो-गो है जहाँ तक मेरा सवाल है। वास्तव में, सभी संभावित समाधानों में मैं नियमित अभिव्यक्ति का उपयोग करने वाले को पसंद करता हूं क्योंकि केवल सभी स्रोत प्लेटफार्मों को सही ढंग से संभालता है।
StringSplitOptions.RemoveEmptyEntries।
यह महान काम करता है और रेगेक्स से तेज है:
input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None)
"\r\n"सरणी में पहले होना महत्वपूर्ण है ताकि इसे एक पंक्ति विराम के रूप में लिया जाए। उपरोक्त इन रेगेक्स समाधानों के समान परिणाम देता है:
Regex.Split(input, "\r\n|\r|\n")
Regex.Split(input, "\r?\n|\r")
सिवाय इसके कि रेगेक्स लगभग 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: ०३.८५,२७,६१६
00: 00: ३१.८०,१७,७२६
00: 00: ३२.५५,५७,१२८
और यहाँ विस्तार विधि है:
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}
\n\rया मैच करेगा \n\nजो सही नहीं है।
Hello\n\nworld\n\nएक किनारे का मामला कैसे है ? यह पाठ के साथ स्पष्ट रूप से एक पंक्ति है, जिसके बाद एक खाली रेखा है, पाठ के साथ एक और पंक्ति है, उसके बाद एक खाली पंक्ति है।
आप Regex.Split का उपयोग कर सकते हैं:
string[] tokens = Regex.Split(input, @"\r?\n|\r");
संपादित करें: |\rमैक लाइन टर्मिनेटर के लिए (पुराने) खाते में जोड़ा गया।
\rलाइन एंडिंग के रूप में उपयोग होते हैं ।
यदि आप खाली लाइनों को रखना चाहते हैं तो बस 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: ०३.९६,०३,८९४
00: 00: ००.००,२९,९९६
00: 00: ०४.८२,२१,९७१
string[] lines = input.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
थोड़ा मुड़, लेकिन इसे करने के लिए एक पुनरावृत्ति ब्लॉक:
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();
private string[] GetLines(string text)
{
List<string> lines = new List<string>();
using (MemoryStream ms = new MemoryStream())
{
StreamWriter sw = new StreamWriter(ms);
sw.Write(text);
sw.Flush();
ms.Position = 0;
string line;
using (StreamReader sr = new StreamReader(ms))
{
while ((line = sr.ReadLine()) != null)
{
lines.Add(line);
}
}
sw.Close();
}
return lines.ToArray();
}
मिक्स्ड लाइन एंडिंग को ठीक से संभालना मुश्किल है । हम जानते हैं, लाइन समाप्ति वर्ण हो सकते हैं "रेखा फ़ीड" (ASCII 10, \n, \x0A, \u000A), "कैरिज वापसी" (ASCII 13, \r, \x0D, \u000D), या उनमें से कुछ संयोजन। डॉस पर वापस जाने पर, विंडोज दो-चरित्र अनुक्रम सीआर-एलएफ का उपयोग करता है \u000D\u000A, इसलिए इस संयोजन को केवल एक पंक्ति का उत्सर्जन करना चाहिए। यूनिक्स एक एकल का उपयोग करता है \u000A, और बहुत पुराने मैक एक एकल \u000Dचरित्र का उपयोग करते हैं । एकल पाठ फ़ाइल के भीतर इन पात्रों के मनमाने मिश्रण का इलाज करने का मानक तरीका निम्नानुसार है:
\u000D\u000A) द्वारा एक CR का तुरंत अनुसरण किया जाता है तो ये दोनों एक साथ होंगे सिर्फ एक लाइन छोड़ते हैं।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;
}