我如何截断一个。net字符串?

我想截断一个字符串,使其长度不超过给定值。我正在向数据库表写入数据,并希望确保写入的值满足列数据类型的约束。

例如,如果我能写以下内容,那就太好了:

string NormalizeLength(string value, int maxLength)
{
return value.Substring(0, maxLength);
}

不幸的是,这会引发异常,因为maxLength通常会超出字符串value的边界。当然,我可以写一个像下面这样的函数,但我希望这样的东西已经存在了。

string NormalizeLength(string value, int maxLength)
{
return value.Length <= maxLength ? value : value.Substring(0, maxLength);
}

执行此任务的难以捉摸的API在哪里?有吗?

411365 次浏览

不幸的是,string上没有Truncate()方法。你必须自己写这种逻辑。然而,你能做的是把它包装在一个扩展方法中,这样你就不必到处复制它:

public static class StringExt
{
public static string Truncate(this string value, int maxLength)
{
if (string.IsNullOrEmpty(value)) return value;
return value.Length <= maxLength ? value : value.Substring(0, maxLength);
}
}

现在我们可以这样写:

var someString = "...";
someString = someString.Truncate(2);
可选的后缀和c#8可空引用类型。
public static class StringExt
{
public static string? Truncate(this string? value, int maxLength, string truncationSuffix = "…")
{
return value?.Length > maxLength
? value.Substring(0, maxLength) + truncationSuffix
: value;
}
}

写:

"abc".Truncate(2);          // "ab…"
"abc".Truncate(3);          // "abc"
((string)null).Truncate(3); // null

在。net中我没有意识到这一点-这里是我的版本,它增加了“…”:

public static string truncateString(string originalString, int length) {
if (string.IsNullOrEmpty(originalString)) {
return originalString;
}
if (originalString.Length > length) {
return originalString.Substring(0, length) + "...";
}
else {
return originalString;
}
}

或者,您可以使用Math.min代替三元操作符

public static class StringExt
{
public static string Truncate( this string value, int maxLength )
{
if (string.IsNullOrEmpty(value)) { return value; }


return value.Substring(0, Math.Min(value.Length, maxLength));
}
}

你可以用LINQ…它消除了检查字符串长度的需要。不可否认,这可能不是最有效的,但很有趣。

string result = string.Join("", value.Take(maxLength)); // .NET 4 Join

string result = new string(value.Take(maxLength).ToArray());

我想我应该加入我的实现,因为我相信它涵盖了其他人所涉及的所有情况,并且以一种简洁的方式做到了这一点,仍然是可读的。

public static string Truncate(this string value, int maxLength)
{
if (!string.IsNullOrEmpty(value) && value.Length > maxLength)
{
return value.Substring(0, maxLength);
}


return value;
}

这个解决方案主要建立在射线的解决方案的基础上,并通过使用关键字将该方法作为扩展方法使用,就像他的解决方案中的LBushkin确实一样。

.NET框架有一个API可以像这样截断字符串:

Microsoft.VisualBasic.Strings.Left(string, int);

但在c#应用程序中,你可能更喜欢编写自己的程序,而不是依赖于Microsoft.VisualBasic.dll,后者存在的主要原因是向后兼容。

我更喜欢jpierson的答案,但这里没有一个例子,我可以看到处理无效的maxLength参数,例如当maxLength <0.

可以选择在try/catch中处理错误,将maxLength参数min限制为0,或者如果maxLength小于0则返回空字符串。

未优化的代码:

public string Truncate(this string value, int maximumLength)
{
if (string.IsNullOrEmpty(value) == true) { return value; }
if (maximumLen < 0) { return String.Empty; }
if (value.Length > maximumLength) { return value.Substring(0, maximumLength); }
return value;
}

以@CaffGeek为例进行简化:

public static string Truncate(this string value, int maxLength)
{
return string.IsNullOrEmpty(value) ? value : value.Substring(0, Math.Min(value.Length, maxLength));
}
public static string Truncate( this string value, int maxLength )
{
if (string.IsNullOrEmpty(value)) { return value; }


return new string(value.Take(maxLength).ToArray());// use LINQ and be happy
}

请注意,截断字符串不仅仅意味着仅以指定的长度切割字符串,而且必须注意不要分割单词。

Eg字符串:这是一个测试字符串。

我想把它剪到11点。如果我们使用上面给出的任何一种方法,结果将是

这是一个

这不是我们想要的

我使用的方法可能也不是那么完美,但它可以处理大多数情况

public string CutString(string source, int length)
{
if (source== null || source.Length < length)
{
return source;
}
int nextSpace = source.LastIndexOf(" ", length);
return string.Format("{0}...", input.Substring(0, (nextSpace > 0) ? nextSpace : length).Trim());
}

为什么不:

string NormalizeLength(string value, int maxLength)
{
//check String.IsNullOrEmpty(value) and act on it.
return value.PadRight(maxLength).Substring(0, maxLength);
}

即在value.Length < maxLength事件中将空格填充到末尾或截断多余部分。

在。net 4.0中,你可以使用Take方法:

string.Concat(myString.Take(maxLength));

没有测试效率!

出于(过度)复杂性的考虑,我将添加重载版本,它将maxLength参数的最后3个字符替换为省略号。

public static string Truncate(this string value, int maxLength, bool replaceTruncatedCharWithEllipsis = false)
{
if (replaceTruncatedCharWithEllipsis && maxLength <= 3)
throw new ArgumentOutOfRangeException("maxLength",
"maxLength should be greater than three when replacing with an ellipsis.");


if (String.IsNullOrWhiteSpace(value))
return String.Empty;


if (replaceTruncatedCharWithEllipsis &&
value.Length > maxLength)
{
return value.Substring(0, maxLength - 3) + "...";
}


return value.Substring(0, Math.Min(value.Length, maxLength));
}

我知道这是一个老问题,但这里有一个很好的解决方案:

public static string Truncate(this string text, int maxLength, string suffix = "...")
{
string str = text;
if (maxLength > 0)
{
int length = maxLength - suffix.Length;
if (length <= 0)
{
return str;
}
if ((text != null) && (text.Length > maxLength))
{
return (text.Substring(0, length).TrimEnd(new char[0]) + suffix);
}
}
return str;
}


var myString = "hello world"
var myTruncatedString = myString.Truncate(4);

返回:你好……

这是我通常使用的代码:

string getSubString(string value, int index, int length)
{
if (string.IsNullOrEmpty(value) || value.Length <= length)
{
return value;
}
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = index; i < length; i++)
{
sb.AppendLine(value[i].ToString());
}
return sb.ToString();
}

似乎还没有人发布这个:

public static class StringExt
{
public static string Truncate(this string s, int maxLength)
{
return s != null && s.Length > maxLength ? s.Substring(0, maxLength) : s;
}
}

使用&&运算符使它比接受的答案略好。

以防这里没有足够的答案,这里是我的:)

public static string Truncate(this string str,
int totalLength,
string truncationIndicator = "")
{
if (string.IsNullOrEmpty(str) || str.Length < totalLength)
return str;


return str.Substring(0, totalLength - truncationIndicator.Length)
+ truncationIndicator;
}

使用方法:

"I use it like this".Truncate(5,"~")
这里是一个vb.net解决方案,标记if(尽管丑陋)语句提高性能,因为当字符串已经小于maxlength时,我们不需要substring语句… 通过使它成为字符串的扩展,它很容易使用…

 <System.Runtime.CompilerServices.Extension()> _
Public Function Truncate(String__1 As String, maxlength As Integer) As String
If Not String.IsNullOrEmpty(String__1) AndAlso String__1.Length > maxlength Then
Return String__1.Substring(0, maxlength)
Else
Return String__1
End If
End Function

TruncateString

public static string _TruncateString(string input, int charaterlimit)
{
int characterLimit = charaterlimit;
string output = input;


// Check if the string is longer than the allowed amount
// otherwise do nothing
if (output.Length > characterLimit && characterLimit > 0)
{
// cut the string down to the maximum number of characters
output = output.Substring(0, characterLimit);
// Check if the character right after the truncate point was a space
// if not, we are in the middle of a word and need to remove the rest of it
if (input.Substring(output.Length, 1) != " ")
{
int LastSpace = output.LastIndexOf(" ");


// if we found a space then, cut back to that space
if (LastSpace != -1)
{
output = output.Substring(0, LastSpace);
}
}
// Finally, add the "..."
output += "...";
}
return output;
}

c# 6的Null传播运算符的类似变体

public static string Truncate(this string value, int maxLength)
{
return value?.Length <= maxLength ? value : value?.Substring(0, maxLength);
}

请注意,我们实际上是在检查value是否为空两次。

截断字符串

public static string TruncateText(string strText, int intLength)
{
if (!(string.IsNullOrEmpty(strText)))
{
// split the text.
var words = strText.Split(' ');


// calculate the number of words
// based on the provided characters length
// use an average of 7.6 chars per word.
int wordLength = Convert.ToInt32(Math.Ceiling(intLength / 7.6));


// if the text is shorter than the length,
// display the text without changing it.
if (words.Length <= wordLength)
return strText.Trim();


// put together a shorter text
// based on the number of words
return string.Join(" ", words.Take(wordLength)) + " ...".Trim();
}
else
{
return "";
}
}
2016年c#字符串仍然没有截断方法。 但是-使用c# 6.0语法:

public static class StringExtension
{
public static string Truncate(this string s, int max)
{
return s?.Length > max ? s.Substring(0, max) : s ?? throw new ArgumentNullException(s);
}
}

它就像一个魔法:

"Truncate me".Truncate(8);
Result: "Truncate"

我知道已经有大量的答案,但我的需要是保持字符串的开始和结束完整,但缩短到最大长度以下。

    public static string TruncateMiddle(string source)
{
if (String.IsNullOrWhiteSpace(source) || source.Length < 260)
return source;


return string.Format("{0}...{1}",
source.Substring(0, 235),
source.Substring(source.Length - 20));
}

用于创建最大长度为260个字符的SharePoint url。

我没有把长度作为参数,因为它是一个常数260。我也没有将第一个子字符串长度作为参数,因为我希望它在特定的点中断。最后,第二个子字符串是源文件的长度——20,因为我知道文件夹的结构。

这可以很容易地适应您的特定需求。

因为性能测试很有趣:(使用Linqpad扩展方法)

var val = string.Concat(Enumerable.Range(0, 50).Select(i => i % 10));


foreach(var limit in new[] { 10, 25, 44, 64 })
new Perf<string> {
{ "newstring" + limit, n => new string(val.Take(limit).ToArray()) },
{ "concat" + limit, n => string.Concat(val.Take(limit)) },
{ "truncate" + limit, n => val.Substring(0, Math.Min(val.Length, limit)) },
{ "smart-trunc" + limit, n => val.Length <= limit ? val : val.Substring(0, limit) },
{ "stringbuilder" + limit, n => new StringBuilder(val, 0, Math.Min(val.Length, limit), limit).ToString() },
}.Vs();

truncate方法“明显”更快。# microoptimization

早期

  • truncate10 5788滴答流逝(0.5788 ms) [10K次,5.788E-05 ms /次]
  • smart-trunc10 8206滴答流逝(0.8206 ms) [10K次,8.206E-05 ms /次]
  • stringbuilder10 10557滴答流逝(1.0557 ms) [10K次,0.00010557 ms /次]
  • concat10 45495滴答流逝(4.5495 ms) [10K次,0.00045495 ms /次]
  • 时间流逝(7.2535 ms) [10K次,0.00072535 ms /次]

晚些时候

  • truncate44 8835滴答流逝(0.8835 ms) [10K次,8.835E-05 ms /次]
  • 13106滴答流逝(1.3106 ms) [10K次,0.00013106 ms /次]
  • smart-trunc44 14821滴答流逝(1.4821毫秒)[10K次,0.00014821毫秒/次]
  • 时间流逝(14.4324 ms) [10K次,0.00144324 ms /次]
  • concat44 174610滴答流逝(17.461毫秒)[每10K次,0.0017461毫秒]

太长时间

  • smart-trunc64 6944滴答流逝(0.6944毫秒)[在10K次中,6.944E-05毫秒每]
  • truncate64 7686滴答流逝(0.7686 ms) [10K次,7.686E-05 ms /次]
  • stringbuilder64 13314滴答流逝(1.3314 ms) [10K次,0.00013314 ms /次]
  • 时间流逝(17.7481 ms) [10K次,0.00177481 ms /次]
  • concat64 241601滴答流逝(24.1601毫秒)[每10K次,0.00241601毫秒]

我是这样写的

value = value.Length > 1000 ? value.Substring(0, 1000) : value;

我建议使用substring方法来实现同样有效的功能。

    // Gets first n characters.
string subString = inputString.Substring(0, n);

这样做的好处是,您可以从任意一侧甚至中间的某个地方拼接字符串,而无需编写额外的方法。希望这对你有所帮助。

额外参考:https://www.dotnetperls.com/substring

我知道这里已经有大量的答案,但这是一个我已经去了,它处理空字符串和传递的长度为负的情况:

public static string Truncate(this string s, int length)
{
return string.IsNullOrEmpty(s) || s.Length <= length ? s
: length <= 0 ? string.Empty
: s.Substring(0, length);
}
作为上述讨论的可能性的补充,我想分享我的解决方案。 它是一个扩展方法,允许null(返回string.Empty),还有第二个. truncate()用于与省略号一起使用。注意,这不是性能优化
public static string Truncate(this string value, int maxLength) =>
(value ?? string.Empty).Substring(0, (value?.Length ?? 0) <= (maxLength < 0 ? 0 : maxLength) ? (value?.Length ?? 0) : (maxLength < 0 ? 0 : maxLength));
public static string Truncate(this string value, int maxLength, string ellipsis) =>
string.Concat(value.Truncate(maxLength - (((value?.Length ?? 0) > maxLength ? ellipsis : null)?.Length ?? 0)), ((value?.Length ?? 0) > maxLength ? ellipsis : null)).Truncate(maxLength);

另一个解决方案:

return input.Substring(0, Math.Min(input.Length, maxLength));

我的观点是,例子长度为30:

  var truncatedInput = string.IsNullOrEmpty(input) ?
string.Empty :
input.Substring(0, Math.Min(input.Length, 30));

在c# 8中,新的范围特性可以被使用…

value = value[..Math.Min(30, value.Length)];

你可以创建一个Truncate扩展方法来比较最大长度和字符串长度,并在需要时调用Substring

如果你想要与Substring相似的空处理行为,不要包含空检查。这样,就像str.Substring(0, 10)str为空时抛出NullReferenceException一样,str.Truncate(10)也会抛出。

public static class StringExtensions
{
public static string Truncate(this string value, int maxLength) =>
value.Length <= maxLength ? value : value.Substring(0, maxLength);
}

流行库人性化有一个截断方法。使用NuGet安装:

Install-Package Humanizer

最近c#中最简单的方法是:

string Trunc(string s, int len) => s?.Length > len ? s.Substring(0, len) : s;

它返回截断值较长的字符串和原始字符串为其他情况-包括空输入-这是由?一元运算符。

基于,这里有两个版本也适用于的“up to”值。第一个函数不允许负数无声地设置为0:

public static string Truncate(this string value, int maxLength)
{
return string.IsNullOrEmpty(value) ?
value :
value.Substring(0, Math.Max(0, Math.Min(value.Length, maxLength)));
}

这是一个循环:

private static int Mod(this int a, int n) => (((a %= n) < 0) ? n : 0) + a;


public static string Truncate(this string value, int maxLength)
{
return string.IsNullOrEmpty(value) ?
value :
value.Substring(0, maxLength.Mod(value.Length));
}

下面是C # 9一行代码:

public static string Truncate(this string value, int maxLength) => value is null or "" || value.Length <= maxLength ? value : value[..maxLength];

所有其他答案都没有考虑到Span的性能,它比。net中字符串类型的Substring的性能更好

如果你还不知道有一个版本的系统。内存(为以下情况提供了Span, ReadOnlySpan, Memory和ReadOnlyMemory:

这样的简单实现可以如下所示:

public static string Truncate(this string value, int maxLength)
{
if (!string.IsNullOrEmpty(value) && value.Length > maxLength)
{
return value.AsSpan(0, maxLength).ToString(); /* Note the use of AsSpan instead of Substring. */
}


return value;
}

该方法理论上可以返回Span<char>,以避免使用Span<T>ToString()成员分配新字符串。

BCL本身在内部使用Span's, ReadOnlySpan's, Memory's和ReadOnlyMemory's来避免问题并帮助优化代码,特别是当你编译编译时已知的数组时,因此使用一个属性返回这个new'd up数组作为ReadOnlySpan<byte>实际上在运行时优化了代码,因为这样JIT就不会对数据调用memcpy,而是使用它,因为它只返回一个Span,因此是一个提前分配的数据窗口,导致:

  1. 更少的分配。
  2. 更少的分配时间。
  3. 使代码总体上更快地使用。