如何统计字符串中的行数?

55

我想从一个字符串中删除文本,并用空行替换每一行。

背景: 我正在编写一个比较函数来比较两个字符串。它们在两个不同的 Web 浏览器中都正常工作并显示出来。当我尝试在浏览器上向下滚动时,两个字符串的长度不同,我想用空行替换要删除的文本,以使我的字符串长度相同。

在下面的代码中,我要计算 aDiff.Text 有多少行。

以下是我的代码:

public string diff_prettyHtmlShowInserts(List<Diff> diffs)
    {
        StringBuilder html = new StringBuilder();

        foreach (Diff aDiff in diffs)
        {
            string text = aDiff.text.Replace("&", "&amp;").Replace("<", "&lt;")
              .Replace(">", "&gt;").Replace("\n", "<br>"); //&para;
            switch (aDiff.operation)
            {

                case Operation.DELETE:                              
                   //foreach('\n' in aDiff.text)
                   // {
                   //     html.Append("\n"); // Would like to replace each line with a blankline
                   // }
                    break;
                case Operation.EQUAL:
                    html.Append("<span>").Append(text).Append("</span>");
                    break;
                case Operation.INSERT:
                    html.Append("<ins style=\"background:#e6ffe6;\">").Append(text)
                        .Append("</ins>");
                    break;
            }
        }
        return html.ToString();
    }

这个可以工作,但我需要为每个旧行添加一个新行,而不是只为整个字符串添加一个新行,该字符串可能有8行。 - Pomster
14个回答

99

方法1:

int numLines = aDiff.text.Length - aDiff.text.Replace _
                   (Environment.NewLine, string.Empty).Length;

方法2:

int numLines = aDiff.text.Split('\n').Length;

两者都可以给你文本中的行数。


抱歉,我还不能点赞,需要更高的声望,但感谢您的帮助 :) - Pomster
12
请注意,就性能而言,拆分字符串将分配空间以创建数组,以便可以计算数组中的最终元素数量。这非常低效,如果您在足够大的输入文本上运行它,实际上会生成OutOfMemoryExceptions。下面@GrahamBedford的答案是最正确的。 - Casey
@Casey 这个答案包括两个选项,其中一个与 Graham 的解决方案相同。但它仍然会分配内存(text.Replace 会分配内存)。 - poncha
就我所看到的,选项1与Graham的方法不同。Graham使用Environment.NewLine.Length进行分割,并添加第一行。在Environment.StackTrace上使用选项1会输出24,而在13行的情况下,Graham的答案也会输出24,因此我同意Casey的观点。 - noontz
3
有一个问题。如果Environment.NewLine是\r\n,那么它的长度为2个字符,这将导致双倍的换行符。因此,以下代码将解决这个问题:int numLines = (aDiff.text.Length - aDiff.text.Replace(Environment.NewLine, string.Empty).Length) / Environment.NewLine.Length; - dkokkinos
显示剩余2条评论

24

您也可以使用 Linq 来计算行的出现次数,代码如下:

int numLines = aDiff.Count(c => c.Equals('\n')) + 1;

虽然晚了一些,但提供了其他答案的选择。


8
只回答不创建新的不必要对象的内容。 - Perdi Estaquel

18

一种不会分配新字符串或字符串数组的变体

private static int CountLines(string str)
{
    if (str == null)
        throw new ArgumentNullException("str");
    if (str == string.Empty)
        return 0;
    int index = -1;
    int count = 0;
    while (-1 != (index = str.IndexOf(Environment.NewLine, index + 1)))
        count++;

   return count + 1;
}

如果一个字符串以换行符结尾,这个方法会报告多一行。对于“1\r\n2\r\n3\r\n”,它会报告4行;对于“1\r\n2\r\n3”,它会报告3行。我的期望是当一个字符串以换行符结尾时,不应该被认为有额外的一行。 - Christopher Hamkins

8

虽然效率不高,但是仍然存在:

var newLineCount = aDiff.Text.Split('\n').Length -1;

它甚至无法编译!var newLineCount = aDiff.Text.Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries).Length; - ilmatte
1
只需使用换行符\n - nunespascal
抱歉,你是对的,它可以编译。然而,Environment.NewLine会将换行符转换为应用程序所运行的平台上的正确换行符:http://msdn.microsoft.com/it-it/library/system.environment.newline.aspx - ilmatte

6
int newLineLen = Environment.NewLine.Length;
int numLines = aDiff.text.Length - aDiff.text.Replace(Environment.NewLine, string.Empty).Length;
if (newLineLen != 0)
{
    numLines /= newLineLen;
    numLines++;
}

略微更加健壮,考虑到第一行不会有换行符。

1
为什么 Environment.NewLine.Length 会返回零?引用自 https://msdn.microsoft.com/en-us/library/system.environment.newline(v=vs.110).aspx :对于非Unix平台,它包含"\r\n"字符串,对于Unix平台,它包含"\n"字符串。 - poncha
我不知道为什么它的长度会是零。但是当我除以某些我不能绝对确定不会为零的东西时,我还是会进行检查。但是,是的,在当前支持的平台上,它不应该是零。 - Graham Bedford

6
我进行了各种方法的性能测试(Split、Replace、对字符进行for循环、Linq.Count),胜者是Replace方法(当字符串小于2KB时,Split方法略快,但差别不大)。
但是接受的答案中有两个错误。一个错误是当最后一行没有以换行符结尾时,它将不会计算最后一行。另一个错误是如果在Windows上读取UNIX换行符的文件,它将不会计算任何行,因为Environment.Newline是\r\n并且不存在(你总可以使用\n,因为它是UNIX和Windows换行符的最后一个字符)。
所以这里有一个简单的扩展方法...
public static int CountLines(this string text)
{
    int count = 0;
    if (!string.IsNullOrEmpty(text))
    {
        count = text.Length - text.Replace("\n", string.Empty).Length;

        // if the last char of the string is not a newline, make sure to count that line too
        if (text[text.Length - 1] != '\n')
        {
            ++count;
        }
    }

    return count;
}

如果性能结果正确,我认为这应该是被接受的答案。但我仍然不明白如何通过字符串进行单个循环可能会更慢,我几乎可以确定使用不安全代码会更快。 - rattrapper

4
高效且内存占用最小化。
Regex.Matches( "Your String" , System.Environment.NewLine).Count ;

当然,我们可以扩展我们的字符串类

using System.Text.RegularExpressions ;

public static class StringExtensions
{
    /// <summary>
    /// Get the nummer of lines in the string.
    /// </summary>
    /// <returns>Nummer of lines</returns>
    public static int LineCount(this string str)
    {
        return Regex.Matches( str , System.Environment.NewLine).Count ;
    }
}

reference : µBio, Dieter Meemken


2
如果最后一行没有'\r\n',它就不会计算最后一行。 - Muhammad Waqas Aziz
1
像 @muh 所说,Count 后面应该加上 + 1。否则,如果只有一行字符串,你将得不到行数为 1。 - Andre Kampling

4
using System.Text.RegularExpressions;

Regex.Matches(text, "\n").Count

我认为计算 '\n' 出现的次数是最有效的方法,考虑到速度和内存使用。

使用 split('\n') 是个坏主意,因为它会创建新的字符串数组,所以在性能和效率上很差!尤其是当你的字符串变大并且包含更多行时。

替换 '\n' 字符为空字符并计算差异也不是很有效,因为它需要执行多个操作,如搜索、创建新的字符串和内存分配等。

你只需要进行一次操作,即搜索。因此,你可以只计算字符串中 '\n' 字符出现的次数,就像 @lokimidgard 建议的那样。

值得一提的是,搜索 '\n' 字符比搜索 "\r\n"(或 Windows 中的 Environment.NewLine)更好,因为前者(即'\n')适用于 Unix 和 Windows 行结尾。


4
晚来了,但我认为这可以处理所有行,甚至是最后一行(至少在Windows中):
Regex.Matches(text, "$", RegexOptions.Multiline).Count; 

3
为了让事情变得简单,我将来自poncha的解决方案放在一个不错的扩展方法中,因此您可以像这样简单地使用它:
int numLines = aDiff.text.LineCount();

代码:

/// <summary>
/// Extension class for strings.
/// </summary>
public static class StringExtensions
{
    /// <summary>
    /// Get the nummer of lines in the string.
    /// </summary>
    /// <returns>Nummer of lines</returns>
    public static int LineCount(this string str)
    {
        return str.Split('\n').Length;
    }
}

玩得开心...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接