Çok satırlı dizeyi satırlara nasıl ayırırsınız?
Bu yolu biliyorum
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
biraz çirkin görünüyor ve boş satırları kaybediyor. Daha iyi bir çözüm var mı?
Çok satırlı dizeyi satırlara nasıl ayırırsınız?
Bu yolu biliyorum
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
biraz çirkin görünüyor ve boş satırları kaybediyor. Daha iyi bir çözüm var mı?
Yanıtlar:
Çirkin görünüyorsa, gereksiz ToCharArray
aramayı kaldırın .
İkisinden birine bölmek istiyorsanız \n
veya \r
iki seçeneğiniz vardır:
Bir dizi değişmezi kullanın - ancak bu size Windows stili satır sonları için boş satırlar verecektir \r\n
:
var result = text.Split(new [] { '\r', '\n' });
Bart tarafından belirtildiği gibi düzenli bir ifade kullanın:
var result = Regex.Split(text, "\r\n|\r|\n");
Boş satırları korumak istiyorsanız, neden C # 'a onları atmasını söylüyorsunuz? ( StringSplitOptions
parametre) - StringSplitOptions.None
bunun yerine kullanın.
Environment.NewLine
endişelendiğim kadarıyla hareketsizdir. Aslında, tüm olası çözümlerden düzenli ifadeleri kullanarak birini tercih ederim çünkü sadece tüm kaynak platformları doğru şekilde işler.
StringSplitOptions.RemoveEmptyEntries
.
Bu harika çalışıyor ve Regex'ten daha hızlı:
input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None)
İlk "\r\n"
önce dizide yer almak önemlidir, böylece bir satır sonu olarak alınır. Yukarıdakiler bu Regex çözümlerinden herhangi biriyle aynı sonuçları vermektedir:
Regex.Split(input, "\r\n|\r|\n")
Regex.Split(input, "\r?\n|\r")
Bunun dışında Regex'in yaklaşık 10 kat daha yavaş olduğu ortaya çıktı. İşte benim testim:
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")
);
Çıktı:
00: 00: 03,8527616
00: 00: 31,8017726
00: 00: 32,5557128
ve işte Uzatma Yöntemi:
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);
}
}
Kullanımı:
input.GetLines() // keeps empty lines
input.GetLines(true) // removes empty lines
[\r\n]{1,2}
\n\r
veya \n\n
doğru olmayan tek satır sonu olarak eşleşecek .
Hello\n\nworld\n\n
Kenar durumu nasıl ? Açıkça metin içeren bir satır, ardından boş bir satır, ardından metin içeren başka bir satır ve ardından boş bir satır gelir.
Regex.Split kullanabilirsiniz:
string[] tokens = Regex.Split(input, @"\r?\n|\r");
Düzenleme: |\r
(eski) Mac satır sonlandırıcıları hesaba eklendi .
\r
satır sonu olarak kullanıldığından OS X stili metin dosyalarında çalışmaz .
Boş satırları tutmak istiyorsanız StringSplitOptions öğesini kaldırın.
var result = input.Split(System.Environment.NewLine.ToCharArray());
Bunu vardı diğer cevabı Jack'in dayalı ama bu bir, cevap , önemli ölçüde daha hızlı olduğu zaman uyumsuz çalıştığı için olsa biraz daha yavaş, tercih edilebilir.
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;
}
}
}
}
Kullanımı:
input.GetLines() // keeps empty lines
input.GetLines(true) // removes empty lines
Ölçek:
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()
);
Çıktı:
00: 00: 03,9603894
00: 00: 00,0029996
00: 00: 04,8221971
Biraz bükülmüş, ancak bunu yapmak için bir yineleyici blok:
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);
}
Ardından şunları arayabilirsiniz:
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();
}
Karışık hat uçlarını doğru şekilde kullanmak zor . Bildiğimiz gibi, hat sonlandırma karakterler "Satır Besleme" olabilir (ASCII 10, \n
, \x0A
, \u000A
), "Satır Başı" (ASCII 13, \r
, \x0D
, \u000D
) veya bunların bazıları kombinasyon. DOS'a geri dönersek, Windows iki karakterlik CR-LF dizisini kullanır \u000D\u000A
, bu nedenle bu kombinasyon yalnızca tek bir satır yaymalıdır. Unix tek \u000A
, çok eski Mac'ler ise tek bir \u000D
karakter kullanır. Bu karakterlerin rastgele karışımlarını tek bir metin dosyasında ele almanın standart yolu aşağıdaki gibidir:
\u000D\u000A
) izliyorsa, bu ikisi birlikte yalnızca bir satırı atlar.String.Empty
hiçbir satır döndürmeyen tek girdidir (herhangi bir karakter en az bir satır içerir)Yukarıdaki kural StringReader.ReadLine ve ilgili işlevlerin davranışını açıklar ve aşağıda gösterilen işlev aynı sonuçları verir. CR / LF'nin herhangi bir keyfi sekansını veya kombinasyonunu doğru bir şekilde işlemek için bu yönergeleri titizlikle uygulayan verimli bir C # satırı kesme işlevidir. Numaralandırılmış satırlar herhangi bir CR / LF karakteri içermiyor. Boş satırlar korunur ve olarak döndürülür 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));
}
Not: StringReader
Her çağrıda bir örnek oluşturmanın ek yükü yoksa, bunun yerine aşağıdaki C # 7 kodunu kullanabilirsiniz . Belirtildiği gibi, yukarıdaki örnek biraz daha verimli olsa da, bu işlevlerin her ikisi de aynı sonuçları verir.
public static IEnumerable<String> Lines(this String s)
{
using (var tr = new StringReader(s))
while (tr.ReadLine() is String L)
yield return L;
}