查找字符串中空行的索引

时间:2012-12-04 01:00:06

标签: c# string

假设我有一个包含文本文件,回车符和制表符的字符串。如何在该字符串中找到第一个空行的索引(包括仅包含行的空格)?

我尝试了什么:

在这种情况下,我有一个工作函数,利用一堆丑陋的代码来查找空行的索引。必须有比 this 更优雅/可读的方式。

要清楚,下面的函数将从提供的'title'的字符串返回到标题后第一个空行的索引处。完整提供,因为大部分都是通过搜索该索引消耗的,并且为了避免任何“为什么在世界上你需要一个空行索引”问题。也是为了抵消XY问题,如果它发生在这里。

(显然工作,尚未测试所有边缘情况)代码:

// Get subsection indicated by supplied title from supplied section
private static string GetSubSectionText(string section, string subSectionTitle)
    {
        int indexSubSectionBgn = section.IndexOf(subSectionTitle);
        if (indexSubSectionBgn == -1)
            return String.Empty;

        int indexSubSectionEnd = section.Length;

        // Find first blank line after found sub-section
        bool blankLineFound = false;
        int lineStartIndex = 0;
        int lineEndIndex = 0;
        do
        {
            string temp;
            lineEndIndex = section.IndexOf(Environment.NewLine, lineStartIndex);

            if (lineEndIndex == -1)
                temp = section.Substring(lineStartIndex);
            else
                temp = section.Substring(lineStartIndex, (lineEndIndex - lineStartIndex));

            temp = temp.Trim();
            if (temp.Length == 0)
            {
                if (lineEndIndex == -1)
                    indexSubSectionEnd = section.Length;
                else
                    indexSubSectionEnd = lineEndIndex;

                blankLineFound = true;
            }
            else
            {
                lineStartIndex = lineEndIndex + 1;
            }
        } while (!blankLineFound && (lineEndIndex != -1));

        if (blankLineFound)
            return section.Substring(indexSubSectionBgn, indexSubSectionEnd);
        else
            return null;
}

后续编辑:

结果(主要基于康斯坦丁的回答)

// Get subsection indicated by supplied title from supplied section
private static string GetSubSectionText(string section, string subSectionTitle)
{
        string[] lines = section.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
        int subsectStart = 0;
        int subsectEnd = lines.Length;

        // Find subsection start
        for (int i = 0; i < lines.Length; i++)
        {
            if (lines[i].Trim() == subSectionTitle)
            {
                subsectStart = i;
                break;
            }
        }

        // Find subsection end (ie, first blank line)
        for (int i = subsectStart; i < lines.Length; i++)
        {
            if (lines[i].Trim().Length == 0)
            {
                subsectEnd = i;
                break;
            }
        }

        return string.Join(Environment.NewLine, lines, subsectStart, subsectEnd - subsectStart);

}

结果与Konstantin的答案之间的主要区别是由于框架版本(我正在使用.NET 2.0,它不支持string []。Take),并利用Environment.NewLine而不是硬编码' \ N”。比原始传球更漂亮,更漂亮,更易读。谢谢大家!

3 个答案:

答案 0 :(得分:4)

您是否尝试过使用String.Split Method

string s = "safsadfd\r\ndfgfdg\r\n\r\ndfgfgg";
string[] lines = s.Split('\n');
int i;
for (i = 0; i < lines.Length; i++)
{
    if (string.IsNullOrWhiteSpace(lines[i]))     
    //if (lines[i].Length == 0)          //or maybe this suits better..
    //if (lines[i].Equals(string.Empty)) //or this
    {
        Console.WriteLine(i);
        break;
    }
}
Console.WriteLine(string.Join("\n",lines.Take(i)));

编辑:回应OP的编辑。

答案 1 :(得分:2)

“空白行”是指只包含空格的行?是的,你应该使用正则表达式;您正在寻找的语法是@"(?<=\r?\n)[ \t]*(\r?\n|$)"

  • (?<= ... )表示前瞻,应该先于您要找的东西。
  • \r?\n表示支持Unix和Windows约定的换行符。
  • 因此,
  • (?<=\r?\n)是前一个换行符的预测。
  • [ \t]*表示零个或多个空格或制表符;这些将匹配空白行的内容(如果有)。
  • (\r?\n|$)表示换行符或文件结尾。

示例:

string source = "Line 1\r\nLine 2\r\n   \r\nLine 4\r\n";
Match firstBlankLineMatch = Regex.Match(source, @"(?<=\r?\n)[ \t]*(\r?\n|$)");
int firstBlankLineIndex = 
    firstBlankLineMatch.Success ? firstBlankLineMatch.Index : -1;

答案 2 :(得分:2)

只是为了好玩:似乎你可以每行重新分配一次字符串。那么,编写一个可以懒惰地计算字符串并返回每一行的迭代器是可能的。例如:

IEnumerable<string> BreakIntoLines(string theWholeThing)
{
    int startIndex = 0;
    int endIndex = 0;
    for(;;)
    {
        endIndex = theWholeThing.IndexOf(Environment.NewLine,startIndex) + Environment.NewLine.Count; //Remember to pick up the newline character(s) too!
        if(endIndex = -1) //Didn't find a newline
        {
            //Return the end part of the string and finish
            yield return theWholeThing.SubString(startIndex);
            yield break;
        }
        else //Found a newline
        {
            //Return where we're at up to the newline
            yield return theWholeThing.SubString(startIndex, endIndex - startIndex);
            startIndex = endIndex;
        }
    }
}

然后你可以将迭代器包装在另一个只返回你关心的行并丢弃其他行的迭代器中。

IEnumerable<string> GetSubsectionLines(string theWholeThing, string subsectionTitle)
{
    bool foundSubsectionTitle = false;
    foreach(var line in BreakIntoLines(theWholeThing))
    {
        if(line.Contains(subSectionTitle))
        {
            foundSubsectionTitle = true; //Start capturing
        }

        if(foundSubsectionTitle)
        {
            yield return line;
        } //Implicit "else" - Just discard the line if we haven't found the subsection title yet

        if(String.IsNullOrWhiteSpace(line))
        {
            //This will stop iterating after returning the empty line, if there is one
            yield break;
        }
    }
}

现在,这种方法(以及其他一些发布的方法)并没有完全符合您的原始代码。例如,如果subsectionTitle中的文本恰好跨越一行,则无法找到它。我们假设规范是以不允许这样的方式编写的。这段代码也会复制原始代码所返回的每一行,这样就好了。

与string.split相比,这样做的唯一好处是,当你完成返回SubSection时,字符串的其余部分不会被评估。对于大多数合理大小的字符串,您可能不在乎。任何“业绩增长”都可能不存在。如果你真的关心性能,你就不会在第一时间复制每一行!

您获得的另一件事(实际上可能很有价值)是代码重用。如果您正在编写一个解析文档的程序,那么能够在单独的行上操作可能会有所帮助。