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

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

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

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

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

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


当前回答

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

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

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

其他回答

作为上述可能性的补充,我想分享一下我的解决方案。 它是一个扩展方法,允许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);
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
    }

所有其他答案都没有考虑到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()成员分配新字符串。

The BCL itself internally uses Span's, ReadOnlySpan's, Memory's, and ReadOnlyMemory's where possible to avoid issues and to help optimize the code, especially when you compile arrays that are known at compile time and so using an property that returns that new'd up array as an ReadOnlySpan<byte> actually optimizes the code at runtime as then the JIT would not call memcpy on the data and instead uses it since it just returns a Span and as such is a window to the data that is already allocated ahead of time resulting in:

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

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

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

因为性能测试很有趣:(使用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();

截断法“明显”更快。# 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 /次]

Late

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毫秒]