web-dev-qa-db-ja.com

複数行の文字列からすべての空白行を効率的に削除する

C#で空白行、つまり文字列から空白のみを含む行を削除する最良の方法は何ですか?それが最良の解決策である場合、私は正規表現を使用して満足しています。

編集:.NET 2.0を使用していることを追加する必要があります。


バウンティの更新:バウンティが授与された後、ロールバックしますが、いくつかの点を明確にしたいと思います。

まず、Perl 5互換正規表現が機能します。これは.NET開発者に限定されません。タイトルとタグは、これを反映するように編集されています。

次に、バウンティの詳細で簡単な例を示しましたが、これはonlyのテストでは満たす必要はありません。あなたの解決策must削除all空白のみで構成される行および最後の改行。正規表現を実行した後、「/ r/n」または空白文字で終わる文字列がある場合、失敗します。

30
FunLovinCoder

空白(タブ、スペース)を含む行を削除したい場合は、以下を試してください:

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();
20
Chris Schmich
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();
}
16
Thomas Levesque

私の頭の上から...

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

これを回す:

 fdasdf 
 asdf 
 [tabs] 
 
 [spaces] 
 
 asdf 
 
 

これに:

 fdasdf 
 asdf 
 asdf 
13
Sky Sanders

LINQの使用:

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

大きな入力や一貫性のない行末を処理している場合は、StringReaderを使用して、代わりにforeachループで上記の古い方法を実行する必要があります。

8
dtb

良くない。 JSON.netを使用してこれを使用します。

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

了解しました。この回答は、賞金で指定された明確な要件に従っています。

また、末尾の改行を削除する必要があります。Regex-fuが失敗します。私の賞金は、このテストに合格する正規表現を私に与えることができるすべての人に当てられます:StripWhitespace( "test\r\n\r\nthis\r\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に置き換えたい3つのオプションパターンがあります。

  1. (?<=\r?\n)(\s*$\r?\n)+-空白のみを含み、前に改行がある1つから無制限の行に一致します(ただし、最初の前の改行には一致しません)。
  2. (?<=\r?\n)(\r?\n)+-改行が先行するコンテンツのない1行から無制限の空行に一致します(ただし、最初の先行する改行には一致しません)。
  3. (\r?\n)+\z-テストされた文字列の最後で無制限の改行と一致します(呼び出したとおりの改行を追跡します)

それはあなたのテストを完全に満足させます!ただし、\r\n\nの両方の改行スタイルも満たします。試してみてください!これが最も正しい答えだと思いますが、より単純な式は指定されたバウンティテストに合格しますが、この正規表現はより複雑な条件に合格します。

編集: @上記の正規表現の最後のパターンマッチには、テスト文字列の末尾に空白を含む複数の改行とは一致しないという潜在的な欠陥が指摘されています。最後のパターンを次のように変更してみましょう。

\b\s+\z\bはWordの境界(Wordの最初または最後)、\ s +は1つ以上の空白文字、\ zはテスト文字列の終わり(「ファイル」の終わり)です。したがって、改行と改行に加えて、タブとスペースを含む、ファイルの最後のあらゆる種類の空白に一致するようになります。 @Willが提供するテストケースの両方をテストしました。

だから今一緒に、それはあるはずです:

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

編集#2:さて、最後の正規表現がカバーしていない可能性のあるケースがもう1つあります。そのケースは、コンテンツの前のファイルの先頭に改行がある入力です。したがって、ファイルの先頭に一致するパターンをもう1つ追加してみましょう。

\A\s+-\Aはファイルの先頭に一致し、\s+は1つ以上の空白文字に一致します。

だから今私たちは持っています:

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

これで、マッチングのための4つのパターンがあります。

  1. ファイルの先頭にある空白
  2. 空白を含む冗長な改行(例:\r\n \r\n\t\r\n
  3. コンテンツのない冗長な改行(例:\r\n\r\n
  4. ファイルの最後の空白
3
BenSwayne

"test\r\n \r\nthis\r\n\r\n"を使用して"test\r\nthis"を出力するソリューションを期待しているウィルの賞金に応えて、私は atomic grouping (aka-を使用するソリューションを考え出しました Nonbacktracking Subexpressions on MSDN)。何が起こっているのかを理解するために、これらの記事を読むことをお勧めします。最終的に、アトミックグループは、他の方法で残された末尾の改行文字を一致させるのに役立ちました。

次のパターンでRegexOptions.Multilineを使用します:

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

これは、他の投稿に対するウィルのコメントから収集したものや、自分のテストケースを含む、いくつかのテストケースの例です。

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*が両方のケースを処理できることに気付きました。

2
Ahmad Mageed

別のオプションを次に示します。StringReaderクラスを使用します。利点:文字列を1回パスすると、中間配列が作成されません。

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;
}
1
Dan Tao

私は一緒に行きます:

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

ホワイトスペースのみの場合は、C#文字列メソッドを使用しないでください。

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

結果は「AOPV1.5」になります

1
dnxit

ウィルの賞金に応えて、ここにテストケースに正しい応答を与えるPerlサブがあります:

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;
1
Toto
string corrected = 
    System.Text.RegularExpressions.Regex.Replace(input, @"\n+", "\n");
1
Adam Robinson

文字列拡張

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);
    }
}
0
Math

私はそれが効率的であるかどうかわかりませんが=)

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

個々のラインに対して作業する場合、ここに簡単なものがあります...

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

ええまあ、結局のところ、私が理解できるすべてのコーナーケースに該当するケースは見つかりませんでした。以下は、除去する正規表現の私の最新の呪文です

  1. 文字列の先頭からのすべての空行
    • 最初の空白以外の行の先頭にスペースを含めない
  2. 最初の非空白行の後、最後の非空白行の前のすべての空行
    • 繰り返しますが、空白以外の行の先頭にあるすべての空白を保持します
  3. 最後の改行を含む、最後の非空白行の後のすべての空行

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

それは本質的に言う:

  • 直後
    • 文字列ORの始まり
    • 最終行の終わり
  • が改行で終わる、可能な限り多くの連続する空白に一致する*
  • [〜#〜]または[〜#〜]
  • 文字列の最後でが終了する改行と可能な限り多くの連続する空白に一致

前半は、最初の非空白行までの文字列の先頭にあるすべての空白、または非空白行間のすべての空白をキャッチします。後半では、空白以外の最後の行の改行を含め、文字列内の残りの空白を引っ掛けます。

助けようとしたすべての人に感謝します。あなたの答えは、マッチングの際に検討する必要があるすべてを考えるのに役立ちました。

*(この正規表現は改行を\r\nと見なすため、文字列のソースに応じて調整する必要があります。一致を実行するためにオプションを設定する必要はありません。)

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