繁体   English   中英

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

[英]Removing all whitespace lines from a multi-line string efficiently

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

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


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

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

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

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

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();
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();
}

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

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

转过来:

fdasdf
asdf
[tabs]

[spaces]  

asdf

进入这个:

fdasdf
asdf
asdf

使用LINQ:

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

如果您正在处理大输入和/或不一致的行结尾,则应使用StringReader并使用foreach循环执行上述old-school。

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

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

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

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

所以这是答案:

(?<=\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 - 在测试字符串的末尾匹配一个到无限制的换行符(在调用它们时跟踪换行符)

这完全满足您的测试! 但也满足\\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. 文件末尾的空格

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

使用此模式使用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*会处理这两种情况。

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

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

结果将是“AOPV1.5”

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

这是另一个选择:使用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方法是.NET 4.0中的新增功能 如果你没有这个,那么自己写一点是微不足道的:

public static bool IsNullOrWhiteSpace(string text) {
    return string.IsNullOrEmpty(text) || text.Trim().Length < 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();
    }
  }

为了回应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;

字符串扩展

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);
    }
}
char[] delimiters = new char[] { '\r', '\n' };
string[] lines = value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
string result = string.Join(Environment.NewLine, lines)

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

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

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

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

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

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

(<=(\\ r \\ n)的|?^)\\ S * \\ r \\ n | \\ r \\ n \\ S * $

基本上说:

  • 之后立马
    • 字符串OR的开头
    • 最后一行的结尾
  • 匹配尽可能多的连续空格,以换行符结尾 *
  • 要么
  • 匹配换行符和尽可能多的连续空格,该空格以字符串末尾结尾

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

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

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

试试这个。

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

string result = s.Replace(Environment.NewLine, String.Empty);
Console.WriteLine(result);
s = Regex.Replace(s, @"^[^\n\S]*\n", "");

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM