ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Humanizer Truncator 类详解:.NET 字符串截断的五种策略与底层实现

Humanizer Truncator 类详解:.NET 字符串截断的五种策略与底层实现 开发工具【免费下载链接】HumanizerHumanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities项目地址https://gitcode.com/gh_mirrors/hu/Humanizer点击查看免费下载导读在 .NET 应用中将过长的字符串截断为指定长度是列表、摘要、日志等场景的高频需求。Humanizer 通过一个静态工具类Truncator对外暴露了五种开箱即用的截断器ITruncator覆盖按字符、按字母数字、按单词、动态长度保留单词等不同截断语义并配合Truncate扩展方法实现一行式调用。本文将围绕 Humanizer.Truncator 的 API 文档 展开结合 Humanizer 仓库的源码实现与单元测试逐一剖析五种截断器的行为差异、底层算法与适用场景帮助你精准选择并正确使用字符串截断能力。一、Truncator 类概览五种截断器的统一入口Truncator是 Humanizer 命名空间下的一个静态类其职责是Gets a ITruncator即作为所有内置截断器的静态工厂/入口点。源码定义位于 src/Humanizer/Truncation/Truncator.csnamespace Humanizer; public static class Truncator { public static ITruncator FixedLength { get; } new FixedLengthTruncator(); public static ITruncator FixedNumberOfCharacters { get; } new FixedNumberOfCharactersTruncator(); public static ITruncator FixedNumberOfWords { get; } new FixedNumberOfWordsTruncator(); public static ITruncator DynamicLengthAndPreserveWords { get; } new DynamicLengthAndPreserveWordsTruncator(); public static ITruncator DynamicNumberOfCharactersAndPreserveWords { get; } new DynamicNumberOfCharactersAndPreserveWordsTruncator(); }五个属性均为ITruncator类型的只读静态属性在类型初始化时各自实例化一个对应的内部实现类。五个属性及其语义对比如下属性截断基准是否保留完整单词对应实现类FixedLength字符串总长度Length否可能在单词中间切断FixedLengthTruncatorFixedNumberOfCharacters字母/数字字符数量否FixedNumberOfCharactersTruncatorFixedNumberOfWords单词数量是按单词计数FixedNumberOfWordsTruncatorDynamicLengthAndPreserveWords字符串总长度是回退到单词边界DynamicLengthAndPreserveWordsTruncatorDynamicNumberOfCharactersAndPreserveWords字母/数字字符数量是DynamicNumberOfCharactersAndPreserveWordsTruncator二、接口契约ITruncator 与 TruncateFrom所有截断器都实现同一个接口 src/Humanizer/Truncation/ITruncator.cspublic interface ITruncator { [return: NotNullIfNotNull(nameof(value))] string? Truncate(string? value, int length, string? truncationString, TruncateFrom truncateFrom TruncateFrom.Right); }接口的四个参数定义了截断操作的全部语义value待截断的字符串允许为nullNotNullIfNotNull保证输入为 null 时返回 null不抛异常length截断目标长度truncationString截断指示符如省略号…、...允许为 null 或空字符串truncateFrom截断方向默认从右侧截断。截断方向由枚举 src/Humanizer/TruncateFrom.cs 控制public enum TruncateFrom { Left, // 从字符串开头左侧截断 Right // 从字符串末尾右侧截断 }Right是默认值也是最常见的方向——保留字符串开头部分在末尾追加省略号Left则用于保留字符串结尾部分如文件名后缀、路径末尾在开头追加省略号。三、扩展方法层一行调用的四种重载Truncator类本身只负责提供截断器实例日常使用中更常见的入口是扩展方法。源码 src/Humanizer/TruncateExtensions.cs 提供了四个重载全部以默认省略号…作为截断指示符// 1. 最简形式固定长度截断默认 FixedLength 右侧 … Text longer than truncate length.Truncate(10) // Text long… // 2. 指定截断器与方向默认仍为 FixedLength … Text longer than truncate length.Truncate(10, Truncator.FixedNumberOfWords) // Text longer… // 3. 自定义截断指示符默认 FixedLength Text longer than truncate length.Truncate(10, ...) // Text long... Text longer than truncate length.Truncate(10, ..., TruncateFrom.Left) // ...ng string // 4. 完全自定义截断指示符 截断器 方向 Text longer than truncate length.Truncate(10, …, Truncator.FixedNumberOfWords, TruncateFrom.Left) // … string最后一个重载是最终实现前面三个只是语法糖最终都汇聚到 TruncateExtensions.cs#L117-L127 中的完整重载先对truncator做空引用校验ArgumentNullException.ThrowIfNull再委托给truncator.Truncate(input, length, truncationString, from)。这意味着接口 扩展方法的组合天然支持自定义截断器——只要实现ITruncator即可无缝接入这一调用链。四、五种截断器逐个拆解4.1 FixedLength按字符串总长度硬性截断源码位于 src/Humanizer/Truncation/FixedLengthTruncator.cs。核心逻辑输入为 null 直接返回 null长度为 0 或不超过length时原样返回若截断指示符为 null 或其长度超过length则退化为纯子串截取右侧截断取value[..length]左侧截断取value[^length..]否则右侧截断返回value[..(length - truncationString.Length)] truncationString左侧截断返回truncationString value[^(length - truncationString.Length)..]。测试用例见 tests/Humanizer.Tests/TruncatorTests.cs验证了其边界行为Text longer than truncate length.Truncate(10, Truncator.FixedLength) // Text long… Text with length equal to truncate length.Truncate(41) // 原样返回 short text.Truncate(20, very long truncation string) // short text指示符超长时不影响短文本 Text longer than truncate length.Truncate(10, trunc) // Text trunc4.2 FixedNumberOfCharacters按字母/数字字符数量截断源码位于 src/Humanizer/Truncation/FixedNumberOfCharactersTruncator.cs。与 FixedLength 的区别在于它不按string.Length计数而是只统计char.IsLetterOrDigit(c)命中的字符字母和数字空格、标点等不计入配额。算法分两阶段预扫描遍历字符串统计字母数字数量若不超过length则原样返回定位截断点从对应方向遍历累计字母数字字符当已处理字母数字数 truncationString.Length length时在此处拼接截断指示符。因此一个含大量空格/标点的字符串可以容纳比length更多的原始字符因为它只要求字母数字达到配额。测试印证Text with more characters than truncate length.Truncate(10, Truncator.FixedNumberOfCharacters) // Text with m…4.3 FixedNumberOfWords按单词数量截断源码位于 src/Humanizer/Truncation/FixedNumberOfWordsTruncator.cs。这里的单词以空白字符char.IsWhiteSpace涵盖空格、换行\n、回车\r、制表符\t等为分隔。实现要点预扫描阶段以由空白切换到非空白计数单词数全程零分配遍历若单词数不超过length则原样返回右侧截断TruncateFromRight正序扫描遇到单词边界空白时累计已处理单词数达到length即在该空白处截断并追加指示符左侧截断TruncateFromLeft逆序扫描达到length个单词后取剩余部分并TrimEnd指示符置于开头。测试用例特别验证了跨空白类型的单词识别Text with more words than truncate length.Truncate(4, Truncator.FixedNumberOfWords) // Text with more words… Words are\nsplit\rby\twhitespace.Truncate(4, Truncator.FixedNumberOfWords) // Words are\nsplit\rby…注意FixedNumberOfWords 只保证保留完整单词单词内部可能含任意字符如标点粘连但绝不会把一个词劈成两半。4.4 DynamicLengthAndPreserveWords动态长度 保留完整单词源码位于 src/Humanizer/Truncation/DynamicLengthAndPreserveWordsTruncator.cs。这是FixedLength 保词的组合仍然以字符串总长度为上限但当截断点落在单词中间时不会硬切而是回退到最近的单词边界把被切到的那个单词整个丢弃再附加截断指示符。关键分支右侧截断TruncateFromRight先算出effectiveLength length - truncationString.Length若effectiveLength处恰好是空白直接在此截断若落在单词中间调用LastIndexOfWhiteSpace向前回退到最近空白若整个字符串都没有空白或回退后前缀为空则只返回截断指示符本身对前缀执行TrimEnd后拼接指示符。左侧截断TruncateFromLeft对称处理从末尾向前扫描空白边界使候选子串长度不超过allowedContentLength若候选单词过长或为空则只返回截断指示符。这一策略的代价是结果长度可能小于length因为丢弃了半个单词但换来的是输出永远以完整单词结尾适合对可读性要求高的摘要场景。4.5 DynamicNumberOfCharactersAndPreserveWords动态字母数字数 保留完整单词源码位于 src/Humanizer/Truncation/DynamicNumberOfCharactersAndPreserveWordTruncator.cs。它是 4.2 与 4.4 的组合以字母数字字符为配额单位同时保证不切碎单词。实现分为TruncateRight与TruncateLeft两个私有方法先用value.Count(char.IsLetterOrDigit)统计总字母数字数若不超过totalLength直接返回原串正序或逆序遍历以累计字母数字数 指示符长度 totalLength定位候选截断点若候选点落在单词中间回退到最近的空白边界lastSpace/nextSpace若不存在可容纳完整单词的边界则只返回指示符或空串最终对前缀/后缀执行TrimEnd/TrimStart后与指示符拼接。由于配额按字母数字计算它对包含大量空格、标点的文本更宽容同时在语义上保证每个完整单词都被保留。五、如何在五种截断器之间做选择结合上面的实现分析可以从三个维度快速决策按长度单位FixedLength和DynamicLengthAndPreserveWords按string.Length计对空格、标点一视同仁FixedNumberOfCharacters和DynamicNumberOfCharactersAndPreserveWords只统计字母数字FixedNumberOfWords按单词计。若要求严格的字节/字符配额如 UI 宽度约束选前者若只是大概这么多文字后者更宽容。是否保词四个 Fixed/Dynamic 中DynamicLengthAndPreserveWords与DynamicNumberOfCharactersAndPreserveWords保证不切碎单词输出以完整单词收尾两个 Fixed 截断器允许在单词中间硬切。截断方向所有截断器都支持TruncateFrom.Right默认保留开头与TruncateFrom.Left保留结尾适合文件名、路径等场景。例如在 UI 列表中展示超长标题时DynamicLengthAndPreserveWords是固定宽度 保词的常用选择而日志前缀截断、保留报文尾部信息时应改用TruncateFrom.Left方向的截断。六、验证与扩展测试如何约束行为仓库的单元测试 tests/Humanizer.Tests/TruncatorTests.cs 使用[Theory][InlineData]逐条验证每种截断器的输入输出对覆盖了null / 空字符串 / 单字符输入的边界行为null 返回 null空串返回空串文本长度等于或小于length时原样返回截断指示符长度超过length时的退化行为退化为纯子串多空白类型\n、\r、\t下的单词计数左/右两个方向的截断结果。这些测试既是 API 的契约文档也是接入自定义ITruncator时的行为参照。如果你需要全新的截断语义如按 CJK 字符宽度截断、按字节数截断只需实现ITruncator.Truncate并复用它通过Truncate扩展方法传入即可无需修改 Humanizer 的任何现有类型。结语Truncator静态类是 Humanizer 字符串截断能力的统一入口五个属性对应五种可复用的截断策略从最朴素的硬切到保留完整单词的智能回退一应俱全配合Truncate扩展方法的四个重载可在不写任何循环的情况下完成绝大多数截断需求。理解每个截断器在长度单位与是否保词两个维度上的差异是写出符合预期的截断代码的关键——这正是 Humanizer 将这一看似简单的操作打磨成完整类型体系的初衷。赞分享开发工具【免费下载链接】HumanizerHumanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities项目地址https://gitcode.com/gh_mirrors/hu/Humanizer点击查看免费下载相关推荐Humanizer 字符串截断指南Truncator 类与五种 ITruncator 实现详解Humanizer 字符串截断指南Truncator 类与五种 ITruncator 实现详解 本文聚焦 Humanizer 库中的 Truncator ht开发工具抖音批量下载从 0 到跑通一条命令存完一个主页抖音批量下载从 0 到跑通一条命令存完一个主页 抖音批量下载去水印不用手动一条条存。douyin downloader 就是干这个的一个开源的无水印下载开发工具Humanizer 字符串截断完全指南Truncator 静态工厂与五种 ITruncator 策略源码解析Humanizer 字符串截断完全指南Truncator 静态工厂与五种 ITruncator 策略源码解析 导读 Humanizer https://lin开发工具上一篇WarriorJS 命令行warriorjs/cli完全指南安装、启动流程与运行参数详解下一篇如何自定义ZCode Hooks7种钩子事件的JSON协议完整解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表