从 C# 中的句子中修剪过长的单词
本文关键字:单词 句子 修剪 | 更新日期: 2023-09-27 18:21:54
我有包含句子的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)));
}
这在理论上是有效的,但如果长字以空格以外的分隔符结尾,它会提供错误的输出。例如:
这是呵呵呵!还有更多。
最终看起来像这样:
这是 sweeeeeeee 还有更多的东西。
更新:
好吧,评论非常好,我意识到这可能有太多的"如果"。如果忘记分隔符可能会更好。相反,如果一个单词被修剪,它可以用三个点显示。下面是一些将单词修剪为最多 5 个字符的示例:
现在启示录! -> 启示录...现在!
启示录! -> 启示录...
!例!-> !考试。。。
这是呵呵呵!还有更多。-> 这是斯威...还有...更多。
编辑:由于要求发生了变化,我将保持正则表达式的精神:
Regex.Replace(original, string.Format(@"('p{{L}}{{{0}}})'p{{L}}+", maxLength), "$1...");
最大长度 = 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.
这样做是找到遵循模式(something)(repeated character more than two times)(something else)
的字符(暂时'w
;应该更改为合理的字符(。对于替换,它使用一个函数来检查长度是否超过所需的最大长度,然后计算重复部分实际上可以容纳在总长度中的长度,然后仅将重复部分削减到该长度。
太乱了。它将无法截断原本很长的单词(例如第二个测试句中的"某物"(,并且构成单词的字符集也需要更改。如果您想走这条路,请考虑这可能是一个起点,但不是完成的解决方案。
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}
,具体取决于您的需求。
我建议将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...
[此答案已更新,以适应问题中要求"..."
]
(我刚刚意识到用 "..."
替换修剪后的子字符串引入了很多错误,修复它们使我的代码有点笨重,抱歉(
试试这个:
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."
您可以使用正则表达式来查找这些重复项:
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
试试这个:
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) + "...";
});
}
}
使用带有零宽度正后看断言的简单正则表达式(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 个单词字符后的任何非空格字符,并将匹配项替换为 ...
。
结果:
这是一段文字,太...长词
这是啪...还有...更多。
天启...现在!
天启...
!唰...
更实用的方法可能是@Curt评论中建议的那样。
我无法立即想到任何连续包含 3 个相同字母的英语单词。 与其简单地在 6 个字符后截掉一个单词,不如尝试这种方法:每当您连续两次遇到同一个字符时,请删除它的任何其他连续出现。 因此,"sweeeeeet"变成了"甜","too"变成了"too"。
这将产生额外的副作用,即将相同标点符号或空格的数量限制为 2,以防有人对这些标点符号或空格过于热心!!!!!!!
如果你想考虑省略号 (...(,那么只需使"最大连续字符"计数 == 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();
}
以下内容会将重复字符的数量限制为 6。因此,对于您的输入"这是sweeeeeeeet!还有更多",输出将是:
"这是斯威伊特!还有更多。
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."