有效地从多行字符串中删除所有空白行

时间:2010-05-19 13:26:23

标签: c# regex string

在C#中删除空行的最佳方法是什么,即只包含字符串空格的行?如果这是最好的解决方案,我很高兴使用正则表达式。

编辑:我应该添加我使用的是.NET 2.0。


Bounty更新:奖励之后我会回滚,但我想澄清一些事情。

首先,任何Perl 5 compat正则表达式都可以。这不仅限于.NET开发人员。标题和标签已经过编辑以反映这一点。

其次,虽然我在赏金细节中给出了一个快速示例,但它不是您必须满足的唯一测试。您的解决方案必须删除除<空> 以及最后一个换行符之外的所有行。如果有一个字符串,在运行正则表达式后,以“/ r / n”或任何空格字符结尾,则会失败。

19 个答案:

答案 0 :(得分:20)

如果要删除包含任何空格(制表符,空格)的行,请尝试:

string fix = Regex.Replace(original, @"^\s*$\n", string.Empty, RegexOptions.Multiline);

编辑(对于@Will):修剪尾随换行符的最简单方法是在结果字符串上使用TrimEnd,例如:

string fix =
    Regex.Replace(original, @"^\s*$\n", string.Empty, RegexOptions.Multiline)
         .TrimEnd();

答案 1 :(得分:16)

string outputString;
using (StringReader reader = new StringReader(originalString)
using (StringWriter writer = new StringWriter())
{
    string line;
    while((line = reader.ReadLine()) != null)
    {
        if (line.Trim().Length > 0)
            writer.WriteLine(line);
    }
    outputString = writer.ToString();
}

答案 2 :(得分:13)

脱离我的头顶......

string fixed = Regex.Replace(input, "\s*(\n)","$1");

转过来:

fdasdf
asdf
[tabs]

[spaces]  

asdf


进入这个:

fdasdf
asdf
asdf

答案 3 :(得分:8)

使用LINQ:

var result = string.Join("\r\n",
                 multilineString.Split(new string[] { "\r\n" }, ...None)
                                .Where(s => !string.IsNullOrWhitespace(s)));

如果您正在处理大型输入和/或不一致的行结尾,您应该使用StringReader并使用foreach循环执行上述旧学校。

答案 4 :(得分:3)

不好。我会使用JSON.net使用这个:

var o = JsonConvert.DeserializeObject(prettyJson);
new minifiedJson = JsonConvert.SerializeObject(o, Formatting.None);

答案 5 :(得分:3)

好的,这个答案符合赏金中规定的明确要求:

  

我还需要删除任何尾随的换行符,而我的正则表达式是   失败。我的赏金给任何可以给我一个通过的正则表达式的人   这个测试:StripWhitespace(“测试\ r \ n \ r \ n这个\ r \ n \ r \ n”)==   “测试\ r \ n这”

所以这是答案:

(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|(\r?\n)+\z

或者@Chris Schmich提供的C#代码:

string fix = Regex.Replace("test\r\n \r\nthis\r\n\r\n", @"(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|(\r?\n)+\z", string.Empty, RegexOptions.Multiline);

现在让我们试着理解它。这里有三个可选模式,我愿意用string.empty替换。

  1. (?<=\r?\n)(\s*$\r?\n)+ - 将一个匹配为仅包含空格并且前面有换行符的无限行(但与前一个换行符不匹配)。
  2. (?<=\r?\n)(\r?\n)+ - 将一行与无限空行匹配,其中没有内容以换行符为前缀(但与前一行换行符不匹配)。
  3. (\r?\n)+\z - 在测试字符串的末尾匹配一个到无限制的换行符(在您调用它们时跟踪换行符)
  4. 完美地满足您的测试!但也满足\r\n\n换行符样式!测试出来!我相信这将是最正确的答案,虽然更简单的表达式将通过您指定的赏金测试,这个正则表达式传递更复杂的条件。

    编辑: @Will指出上述正则表达式的最后一个模式匹配中的潜在缺陷,因为它不会匹配测试字符串末尾包含空格的多个换行符。所以让我们改变最后一个模式:

    \b\s+\z \ b是单词边界(单词的开头或结尾),\ s +是一个或多个空格字符,\ z是测试字符串的结尾(“文件结束”) “)。所以现在它将匹配文件末尾的任何分类的空格,包括选项卡和空格以及回车和换行符。我测试了@Will提供的两个测试用例。

    现在一起,它应该是:

    (?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|\b\s+\z
    

    编辑#2:好的还有一个可能的案例@Wil发现最后一个正则表达式没有涵盖。这种情况是在任何内容之前在文件开头有换行符的输入。所以我们再添加一个模式来匹配文件的开头。

    \A\s+ - \A匹配文件的开头,\s+匹配一个或多个空格字符。

    所以现在我们得到了:

    \A\s+|(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|\b\s+\z
    

    现在我们有四种匹配模式:

    1. 文件开头的空白
    2. 包含空格的冗余换行符(例如:\r\n \r\n\t\r\n
    3. 没有内容的冗余换行符(例如:\r\n\r\n
    4. 文件末尾的空白

答案 6 :(得分:2)

为了回应Will的赏金,它需要一个需要"test\r\n \r\nthis\r\n\r\n"和输出"test\r\nthis"的解决方案,我想出了一个利用atomic grouping的解决方案(又名{{3在MSDN上)。我建议阅读这些文章,以便更好地了解正在发生的事情。最终,原子组帮助匹配原本留下的后续换行符。

使用RegexOptions.Multiline这种模式:

^\s+(?!\B)|\s*(?>[\r\n]+)$

以下是一些测试用例的示例,其中包括一些我从Will对其他帖子的评论以及我自己的评论中收集到的一些测试用例。

string[] inputs = 
{
    "one\r\n \r\ntwo\r\n\t\r\n \r\n",
    "test\r\n \r\nthis\r\n\r\n",
    "\r\n\r\ntest!",
    "\r\ntest\r\n ! test",
    "\r\ntest \r\n ! "
};
string[] outputs = 
{
    "one\r\ntwo",
    "test\r\nthis",
    "test!",
    "test\r\n ! test",
    "test \r\n ! "
};

string pattern = @"^\s+(?!\B)|\s*(?>[\r\n]+)$";

for (int i = 0; i < inputs.Length; i++)
{
    string result = Regex.Replace(inputs[i], pattern, "",
                                  RegexOptions.Multiline);
    Console.WriteLine(result == outputs[i]);
}

编辑:为了解决模式无法使用空格和换行符混合清理文本的问题,我将\s*添加到正则表达式的最后一个替换部分。我之前的模式是多余的,我意识到\s*会处理这两种情况。

答案 7 :(得分:1)

string corrected = 
    System.Text.RegularExpressions.Regex.Replace(input, @"\n+", "\n");

答案 8 :(得分:1)

这是另一种选择:使用StringReader类。优点:一次遍历字符串,不创建中间数组。

public static string RemoveEmptyLines(this string text) {
    var builder = new StringBuilder();

    using (var reader = new StringReader(text)) {
        while (reader.Peek() != -1) {
            string line = reader.ReadLine();
            if (!string.IsNullOrWhiteSpace(line))
                builder.AppendLine(line);
        }
    }

    return builder.ToString();
}

注意:IsNullOrWhiteSpace方法为new in .NET 4.0。如果你没有这个,那么自己写一点是微不足道的:

public static bool IsNullOrWhiteSpace(string text) {
    return string.IsNullOrEmpty(text) || text.Trim().Length < 1;
}

答案 9 :(得分:1)

我会选择:

  public static string RemoveEmptyLines(string value) {
    using (StringReader reader = new StringReader(yourstring)) {
      StringBuilder builder = new StringBuilder();
      string line;
      while ((line = reader.ReadLine()) != null) {
        if (line.Trim().Length > 0)
          builder.AppendLine(line);
      }
      return builder.ToString();
    }
  }

答案 10 :(得分:1)

为了回应Will的赏金,这里有一个Perl sub,可以对测试用例给出正确的答案:

sub StripWhitespace {
    my $str = shift;
    print "'",$str,"'\n";
    $str =~ s/(?:\R+\s+(\R)+)|(?:()\R+)$/$1/g;
    print "'",$str,"'\n";
    return $str;
}
StripWhitespace("test\r\n \r\nthis\r\n\r\n");

<强>输出:

'test

this

'
'test
this'

为了不使用\R,请将其替换为[\r\n]并反转替代方案。这个产生相同的结果:

$str =~ s/(?:(\S)[\r\n]+)|(?:[\r\n]+\s+([\r\n])+)/$1/g;

不需要特殊配置和多线支持。不过,如果强制要求,您可以添加s标记。

$str =~ s/(?:(\S)[\r\n]+)|(?:[\r\n]+\s+([\r\n])+)/$1/sg;

答案 11 :(得分:1)

如果只有White空格,为什么不使用C#字符串方法

    string yourstring = "A O P V 1.5";
    yourstring.Replace("  ", string.empty);

结果将是“AOPV1.5”

答案 12 :(得分:0)

我不确定是否有效,但=)

  List<string> strList = myString.Split(new string[] { "\n" }, StringSplitOptions.None).ToList<string>();
  myString = string.Join("\n", strList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList());

答案 13 :(得分:0)

字符串扩展

public static string UnPrettyJson(this string s)
{
    try
    {
        // var jsonObj = Json.Decode(s);
        // var sObject = Json.Encode(value);   dont work well with array of strings c:['a','b','c']

        object jsonObj = JsonConvert.DeserializeObject(s);
        return JsonConvert.SerializeObject(jsonObj, Formatting.None);
    }
    catch (Exception e)
    {
        throw new Exception(
            s + " Is Not a valid JSON ! (please validate it in http://www.jsoneditoronline.org )", e);
    }
}

答案 14 :(得分:0)

的Eh。好吧,经过这一切,我找不到一个可以击中我能想到的所有角落情况。以下是我最新的正则表达式删除

  1. 字符串开头的所有空行
    • 在第一个非空白行的开头不包括任何空格
  2. 第一个非空白行之后和最后一个非空白行之前的所有空行
    • 同样,在任何非空白行的开头保留所有空格
  3. 最后一个非空白行之后的所有空行,包括最后一个换行符
  4.   

    (?&LT; =(\ r \ n)的| ^)\ S * \ r \ n | \ r \ n \ S * $

    基本上说:

    • 之后立刻
      • 字符串OR的开头
      • 最后一行的结尾
    • 尽可能多地匹配以换行符结尾的连续空格 *
    • 匹配换行符和尽可能多的连续空格在字符串末尾结束

    前半部分捕获字符串开头的所有空格,直到第一个非空白行,或非空白行之间的所有空格。下半部分阻塞了字符串中剩余的空格,包括最后一个非空白行的换行符。

    感谢所有试图提供帮助的人;你的答案帮助我思考了匹配时我需要考虑的一切。

    *(此正则表达式认为换行符为\r\n,因此必须根据字符串的来源进行调整。不需要设置选项以运行匹配。)

答案 15 :(得分:0)

如果针对每一条线工作,这是一件简单的事情......

(^\s+|\s+|^)$

答案 16 :(得分:0)

char[] delimiters = new char[] { '\r', '\n' };
string[] lines = value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
string result = string.Join(Environment.NewLine, lines)

答案 17 :(得分:-1)

试试这个。

string s = "Test1" + Environment.NewLine + Environment.NewLine + "Test 2";
Console.WriteLine(s);

string result = s.Replace(Environment.NewLine, String.Empty);
Console.WriteLine(result);

答案 18 :(得分:-2)

s = Regex.Replace(s, @"^[^\n\S]*\n", "");

[^\n\S]匹配任何不是换行符或非空白字符的字符 - 因此,除了\n之外的任何空白字符。但很可能你唯一需要担心的是空格,制表符和回车符,所以这也应该有效:

s = Regex.Replace(s, @"^[ \t\r]*\n", "");

如果你想让它抓住最后一行,没有最后的换行符:

s = Regex.Replace(s, @"^[ \t\r]*\n?", "");