6

文を含む C# 文字列があります。これらの文は問題ない場合もあれば、ユーザーが生成したランダムな文字である場合もあります。私がやりたいのは、これらの文の中の単語を削除することです。たとえば、次の文字列があるとします。

var stringWithLongWords = "Here's a text with tooooooooooooo long words";

これをフィルターを通して実行したいと思います:

var trimmed = TrimLongWords(stringWithLongWords, 6);

そして、すべての単語に最大 6 文字しか含めることができない出力を取得するには、次のようにします。

"Here's a text with tooooo long words"

良いパフォーマンスでこれを行う方法はありますか? これを自動的に処理できる.NETに何かありますか?

現在、次のコードを使用しています。

    private static string TrimLongWords(string original, int maxCount)
    {
        return string.Join(" ", original.Split(' ').Select(x => x.Substring(0, x.Length > maxCount ? maxCount : x.Length)));
    }

これは理論的には機能しますが、長い単語がスペース以外の区切り文字で終わっている場合、悪い出力が得られます。例えば:

これはスウィーエエエエエエエエエエエエエエエエエエエッ!そしてもっと何か。

最終的には次のようになります。

これはsweeeeeeeです そして何かもっと。

アップデート:

OK、コメントがとても良かったので、これには「もしも」が多すぎるかもしれないことに気付きました。セパレーターを忘れた方がいいかもしれません。代わりに、単語がトリミングされた場合、3 つのドットで表示される可能性があります。最大 5 文字に短縮された単語の例を次に示します。

地獄の黙示録!-> アポカ…今!

黙示録!-> アポカ...

!例!-> !試験...

これはスウィーエエエエエエエエエエエエエエエエエエエッ!そしてもっと何か。- > これはスィーイー...そして何か...もっと。

4

9 に答える 9

4

編集:要件が変更されたため、正規表現を使用して精神を維持します:

Regex.Replace(original, string.Format(@"(\p{{L}}{{{0}}})\p{{L}}+", maxLength), "$1...");

maxLength = 6の出力:

Here's a text with tooooo... long words
This is sweeee...! And someth... more.

以下の古い回答は、少し面倒ですが、アプローチが気に入ったためです:-)。


それを行うために、正規表現の置き換えを少しハックしました。今のところ PowerShell にあります (プロトタイピング用です。後で C# に変換します)。

'Here''s a text with tooooooooooooo long words','This is sweeeeeeeeeeeeeeeet! And something more.' |
  % {
    [Regex]::Replace($_, '(\w*?)(\w)\2{2,}(\w*)',
      {
        $m = $args[0]
        if ($m.Value.Length -gt 6) {
          $l = 6 - $m.Groups[1].Length - $m.Groups[3].Length
          $m.Groups[1].Value + $m.Groups[2].Value * $l + $m.Groups[3].Value
        }
      })
  }

出力は次のとおりです。

Here's a text with tooooo long words
This is sweeet! And something more.

\wこれが行うことは、 pattern に従う一連の文字を見つけることです (今のところ; 適切なものに変更する必要があります) (something)(repeated character more than two times)(something else)。置換では、長さが目的の最大長を超えているかどうかをチェックする機能を使用し、次に、繰り返し部分が実際に全長に収まる長さを計算し、繰り返し部分のみをその長さに切り詰めます。

ぐちゃぐちゃです。そうでなければ非常に長い単語 (例: 2 番目のテスト文の »something«) を切り詰めることができず、単語を構成する文字セットも変更する必要があります。そのルートに進みたい場合は、これが出発点になる可能性がありますが、完成したソリューションではないと考えてください。

C# コード:

public static string TrimLongWords(this string original, int maxCount)
{
    return Regex.Replace(original, @"(\w*?)(\w)\2{2,}(\w*)",
        delegate(Match m) {
            var first = m.Groups[0].Value;
            var rep = m.Groups[1].Value;
            var last = m.Groups[2].Value;
            if (m.Value.Length > maxCount) {
                var l = maxCount - first.Length - last.Length;
                return first + new string(rep[0], l) + last;
            }
            return m.Value;
        });
}

\p{L}文字クラスのより良いオプションは、ニーズに応じて、おそらく のようなものです。

于 2013-07-11T11:43:08.430 に答える
2

正規表現を使用して、これらの繰り返しを見つけることができます。


string test = "This is sweeeeeeeeeeeeeeeet! And sooooooomething more.";
string result = Regex.Replace(test, @"(\w)\1+", delegate(Match match)
{
    string v = match.ToString();
    return v[0].ToString();
});

結果は次のようになります。


This is swet! And something more.

また、スペルチェッカー サービスで操作された単語をチェックすることもできます: http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:web_service

于 2013-07-11T11:56:29.090 に答える
2

これを試して:

class Program
{
    static void Main(string[] args)
    {
        var stringWithLongWords = "Here's a text with tooooooooooooo long words";
        var trimmed = TrimLongWords(stringWithLongWords, 6);
    }

    private static string TrimLongWords(string stringWithLongWords, int p)
    {
        return Regex.Replace(stringWithLongWords, String.Format(@"[\w]{{{0},}}", p), m =>
        {
            return m.Value.Substring(0, p-1) + "...";
        });
    }
}
于 2013-07-11T12:01:37.503 に答える
2

より実用的なアプローチは、@Curt がコメントで提案したものです。

同じ文字が3つ連続する英単語がすぐに思い浮かびません。単純に 6 文字の単語を切り捨てるのではなく、次の方法を試してみてください。同じ文字が 2 回続けて出てきたら、その文字がさらに連続して出現する場合は削除します。したがって、「sweeeeeet」は「sweet」になり、「tooooooo」は「too」になります。

これには、誰かがそれらに過度に熱心な場合に備えて、同じ句読点または空白の数を2に制限するという追加の副作用があります!!!!!!!!

省略記号 (...) を考慮したい場合は、「最大連続文字数」を 2 ではなく 3 にします。

于 2013-07-11T12:43:45.247 に答える
2

ゼロ幅の肯定的な後読みアサーションで単純な正規表現を使用する ( LinqPad対応のサンプル コード):

void Main()
{
    foreach(var s in new [] { "Here's a text with tooooooooooooo long words", 
                              "This is sweeeeeeeeeeeeeeeet! And something more.",
                              "Apocalypse now!",
                              "Apocalypse!",
                              "!Example!"})
        Regex.Replace(s, @"(?<=\w{5,})\S+", "...").Dump();

}

5 つの単語文字の後にスペース以外の文字を探し、一致を に置き換え...ます。

結果:

これはあまりにも...長い言葉を含むテキストです
。これは甘いです...そしていくつか...もっと。
アポカ…今!
アポカ...
!例...

于 2013-07-11T12:07:32.207 に答える
1

次の例では、繰り返し文字の数を 6 に制限しています。出力は次のようになります。

「これはスウィート!そしてもっと何か。」

string s = "heloooooooooooooooooooooo worrrllllllllllllld!";
char[] chr = s.ToCharArray();
StringBuilder sb = new StringBuilder();
char currentchar = new char();
int charCount = 0;

foreach (char c in chr)
{
     if (c == currentchar)
     {
         charCount++;
     }
     else
     {
         charCount = 0;
     }

     if ( charCount < 6)
     {
         sb.Append(c);
     }

     currentchar = c;
 }

 Console.WriteLine(sb.ToString());
 //Output heloooooo worrrlllllld!

編集: 6 文字を超える単語を切り捨てます:

string s = "This is sweeeeeeeeeeeeeeeet! And something more.";
string[] words = s.Split(' ');
StringBuilder sb = new StringBuilder();

foreach (string word in words)
{
    char[] chars = word.ToCharArray();
    if (chars.Length > 6)
    {
        for (int i = 0; i < 6; i++)
        {
            sb.Append(chars[i]);
        }
        sb.Append("...").Append(" ");
    }
    else { sb.Append(word).Append(" "); }
}

sb.Remove(sb.Length - 1, 1);
Console.WriteLine(sb.ToString());
//Output: "This is sweeee... And someth... more."
于 2013-07-11T12:10:26.970 に答える