从C#中的句子中修剪太长的单词?

时间:2013-07-11 11:33:45

标签: c#

我有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)));
    }

理论上有效,但如果长词以空格之外的分隔符结尾,则会提供错误的输出。例如:

  

这是sweeeeeeeeeeeeeeeetet!还有更多。

看起来像这样:

  

这是sweeeeeeee等等。

更新:

好的,评论非常好,我意识到这可能有太多“假设”。如果忘记分隔符也许会更好。相反,如果一个单词被修剪,它可以用三个点显示。以下是一些修剪为最多5个字符的示例:

天启现在! - >阿波卡......现在!

启示! - > Apoca ...

!例! - > !考试......

这是sweeeeeeeeeeeeeeeetet!还有更多。 - >这太好了......还有一些......更多。

9 个答案:

答案 0 :(得分: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模式的字符(现在(something)(repeated character more than two times)(something else);应该更改为合理的字符串)。对于替换,它使用一个函数来检查它的长度是否超过所需的最大长度,然后它计算重复部分实际上仍然适合总长度的时间长度,然后仅将重复部分减少到该长度。

太乱了。它将无法截断非常长的单词(例如第二个测试句中的“某些东西”),并且构成单词的字符集也需要更改。如果您想要走这条路线,那么这可能是一个起点,但不是一个完整的解决方案。

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}

答案 1 :(得分:4)

我建议使用StringBuilder和循环:

public string TrimLongWords(string input, int maxWordLength)
{
    StringBuilder sb = new StringBuilder(input.Length);
    int currentWordLength = 0;
    bool stopTripleDot = false;
    foreach (char c in input)
    {
        bool isLetter = char.IsLetter(c);
        if (currentWordLength < maxWordLength || !isLetter)
        {
            sb.Append(c);
            stopTripleDot = false;
            if (isLetter)
                currentWordLength++;
            else
                currentWordLength = 0;
        }
        else if (!stopTripleDot)
        {
            sb.Append("...");
            stopTripleDot = true;
        }
    }
    return sb.ToString();
}

这比Regex或Linq更快 maxWordLength == 6的预期结果:

"UltraLongWord"           -> "UltraL..."
"This-is-not-a-long-word" -> "This-is-not-a-long-word"

边缘案例maxWordLength == 0会导致:

"Please don't trim me!!!" -> "... ...'... ... ...!!!" // poor, poor string...

[此答案已更新,以适应问题中所要求的"..."]

(我刚刚意识到用"..."替换修剪过的子串已经引入了很多错误,修复它们会让我的代码变得有点笨重,抱歉)

答案 2 :(得分:2)

试试这个:

private static string TrimLongWords(string original, int maxCount)
{
   return string.Join(" ", 
   original.Split(' ')
   .Select(x => { 
     var r = Regex.Replace(x, @"\W", ""); 
     return r.Substring(0, r.Length > maxCount ? maxCount : r.Length) + Regex.Replace(x, @"\w", ""); 
   }));
}

然后TrimLongWords("This is sweeeeeeeeeeeeeeeet! And something more.", 5)变为"This is sweee! And somet more."

答案 3 :(得分:2)

这比正则表达式或Linq方法更有效。但是,它不会按字词分割或添加...。白色空间(包括换行符或制表符)也应缩短。

public static string TrimLongWords(string original, int maxCount)
{
    if (null == original || original.Length <= maxCount) return original;

    StringBuilder builder = new StringBuilder(original.Length);
    int occurence = 0;

    for (int i = 0; i < original.Length; i++)
    {
        Char current = original[i];
        if (current == original.ElementAtOrDefault(i-1))
            occurence++;
        else
            occurence = 1;
        if (occurence <= maxCount)
            builder.Append(current);
    }
    return builder.ToString();
}

答案 4 :(得分: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

答案 5 :(得分: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) + "...";
        });
    }
}

答案 6 :(得分: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个字符后面的任何非空格字符,并将匹配替换为...

结果:

  

这是一个太长的文字......长话词   这很好......而且有些......更多   阿波卡......现在!
  Apoca ...
  !例子......

答案 7 :(得分:2)

@Curt在​​评论中建议采用更实用的方法。

我无法立即想到任何包含3个相同字母的英文单词。您可以尝试这种方法,而不是简单地删除6个字符后的单词:每当您连续两次遇到相同的字符时,请删除它的任何其他连续出现。因此,“sweeeeeet”变得“甜蜜”,“tooooooo”变得“太”。

这会产生额外的副作用,即将相同标点符号或空白区域的数量限制为2,以防有人过于热心于那些!!!!!!!!

如果您想考虑省略号(...),那么只需将“最大连续字符数”计算为== 3,而不是2。

答案 8 :(得分:1)

以下将重复字符的数量限制为6.因此,对于您的输入“这是sweeeeeeeeeeeeeeeet!还有更多内容。”输出将是:

“这是sweeeeeet!还有更多。”

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."