
开发工具【免费下载链接】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 提供的DynamicNumberOfCharactersAndPreserveWordsTruncator截断器它能在只统计字母与数字的计数规则下把字符串截断到指定数量同时保证永远不把一个完整的单词拦腰截断并在空间不足时优雅地退化为只输出省略号分隔符。读完本文你将掌握该截断器的类声明、Truncate方法全部参数语义、左右两个方向的截断算法原理、与其它内置截断器的差异以及基于 TruncateExtensions.cs 的完整调用方式与测试驱动的边界行为。一、类概述一种按字符预算、按单词落刀的截断策略在 src/Humanizer/Truncation/DynamicNumberOfCharactersAndPreserveWordTruncator.cs 的类注释中官方给出了它的核心语义将字符串截断到固定数量的字母或数字letters or digits而不是固定的字符数保留完整单词永远不把一个词从中间切开如果一个完整的单词连同分隔符无法放入剩余空间则只返回分隔符当从左侧截断时若能够保留一个完整单词则分隔符前置否则同样只返回分隔符允许的数量allowed count只通过统计字母/数字得到。从源码结构看这个类的动态体现在两处一是计数对象是动态扫描出来的字母/数字空格、标点、符号不计入预算二是截断点会动态回溯到最近的空白边界。它因此与按固定字符数硬切的FixedLengthTruncator、FixedNumberOfCharactersTruncator形成了明确的分工前者保证字数上限后者保证语义完整。二、类声明、继承与接口契约API 文档给出的完整声明如下public class DynamicNumberOfCharactersAndPreserveWordsTruncator : Humanizer.ITruncator继承链System.Object→DynamicNumberOfCharactersAndPreserveWordsTruncator实现的接口ITruncator该接口定义了唯一的契约方法public interface ITruncator { [return: NotNullIfNotNull(nameof(value))] string? Truncate(string? value, int length, string? truncationString, TruncateFrom truncateFrom TruncateFrom.Right); }接口层面的两个约定值得注意[return: NotNullIfNotNull(nameof(value))]只要传入的value不为null返回值就一定不为null传入null则返回null这在后续的空值处理逻辑中得到了兑现所有截断器统一接受四个参数长度、截断字符串、截断方向方便在 Truncator.cs 中通过静态属性统一暴露、按需切换。Humanizer 通过 Truncator.cs 暴露了本类的单例入口public static ITruncator DynamicNumberOfCharactersAndPreserveWords { get; } new DynamicNumberOfCharactersAndPreserveWordsTruncator();三、Truncate 方法签名与四个参数的精确语义API 文档列出的完整方法签名如下public string? Truncate(string? value, int totalLength, string? delimiter, Humanizer.TruncateFrom truncateFrom Humanizer.TruncateFrom.Right);3.1 参数表参数类型默认值含义valuestring?无待截断的原始字符串允许为nulltotalLengthint无允许的字母/数字数量上限不含未被计数的空白与符号delimiterstring?无截断指示符如…、...允许为null按空串处理truncateFromTruncateFromTruncateFrom.Right截断方向从右侧或左侧截断其中TruncateFrom定义在 TruncateFrom.cs只有两个取值Left从字符串开头截掉字母与Right从字符串末尾截掉字母。3.2 返回值返回string?截断后的字符串当value为null时返回null。方法实现了 ITruncator.Truncate 接口。3.3 入口处的三层快速判断从 DynamicNumberOfCharactersAndPreserveWordTruncator.cs 的实现可以看到Truncate入口先做三类短路判断避免无谓的扫描value null→ 返回null对应NotNullIfNotNull约定value.Length 0→ 原样返回空串统计全部字母/数字数量var totalAlpha value.Count(char.IsLetterOrDigit);若totalAlpha totalLength说明内容在预算之内完整返回原字符串。注意第三步是本类与其它截断器的关键分水岭比较对象是字母/数字总数而非字符串长度因此一段含大量空格或标点的文本即使字符数超过totalLength也可能因为字母/数字数量未超标而原样返回。delimiter还有一层特殊处理delimiter ?? string.Empty;——null分隔符被当作空串而当分隔符自身长度超过totalLength且字符串本就放得下时直接回退为普通子串返回源码 L28-L31。四、算法原理从右侧截断的完整单词回溯当需要真正截断时方法按方向委托给私有静态方法TruncateRight或TruncateLeft源码 L41-L43。4.1 TruncateRight正向扫描 回溯到最近空白TruncateRight源码 L46-L133的算法可拆解为四步正向扫描从左到右遍历记录最近一次出现空白的位置lastSpace同时累计字母/数字数alphaCount当alphaCount delimiter.Length totalLength或alphaCount totalLength时记录candidateIndex并停止整词性校正若候选位置落在单词中间candidateIndex处不是空白则回退到lastSpace即把当前不完整的单词整个丢弃若此前从未出现过空白整个前缀只有一个超长单词则直接返回delimiter——这就是文档所述一个完整单词都无法放入时只返回分隔符去尾空白对前缀执行TrimEnd()预算再校验重新统计前缀中的字母/数字数prefixLength分四种情况收尾prefixLength 0→ 返回delimiter分隔符超过预算且前缀也超预算 → 返回空串分隔符比前缀还长且超预算 → 返回前缀本身前缀加分隔符超出预算 → 只返回delimiter否则 → 返回prefix delimiter。4.2 TruncateLeft反向扫描 前进到下一个空白TruncateLeft源码 L135-L221是镜像对称的实现反向扫描从右往左累计字母/数字数并记录最近靠右的空白位置nextSpace整词性校正若候选位置把单词切开则前进到nextSpace 1把不完整的单词整体剔除若没有可用的空白边界则只返回delimiter去首空白对后缀执行TrimStart()预算再校验逻辑与右侧完全对称最终返回delimiter suffix。可以看出只统计字母/数字这一规则贯穿了扫描、校正、预算校验三个环节保证最终结果中可计数的字符数严格不超过totalLength。五、实战用法四种重载与常见调用形态日常使用并不需要直接new这个类而是通过 TruncateExtensions.cs 提供的扩展方法与 Truncator.cs 的静态属性组合调用。扩展方法共有四种重载形态5.1 使用默认省略号 指定截断器最常用using Humanizer; var result Text with more characters than truncate length .Truncate(10, Truncator.DynamicNumberOfCharactersAndPreserveWords); // 结果Text with…该重载的签名是Truncate(this string? input, int length, ITruncator truncator, TruncateFrom from TruncateFrom.Right)TruncateExtensions.cs L56-L57默认截断指示符为…省略号。5.2 自定义分隔符Text with more characters than truncate length .Truncate(10, ..., Truncator.DynamicNumberOfCharactersAndPreserveWords); // 结果Text...对应重载Truncate(this string? input, int length, string? truncationString, ITruncator truncator, TruncateFrom from TruncateFrom.Right)TruncateExtensions.cs L117这是自由度最高的形态分隔符可以是…、...、---等任意字符串甚至可以是空串。5.3 从左侧截断Text with more characters than truncate length .Truncate(10, Truncator.DynamicNumberOfCharactersAndPreserveWords, TruncateFrom.Left); // 结果…length Text with more characters than truncate length .Truncate(10, ..., Truncator.DynamicNumberOfCharactersAndPreserveWords, TruncateFrom.Left); // 结果...length从左侧截断时保留的是字符串的尾部分隔符被前置若尾部的第一个单词也放不下则只返回分隔符如A Text ...截到 3 个字母时结果为…。5.4 扩展方法内部的空值与空截断器保护扩展方法在委托给ITruncator之前会先执行ArgumentNullException.ThrowIfNull(truncator)与input null短路TruncateExtensions.cs L119-L126因此null输入在任何重载下都会得到null返回值不会抛出NullReferenceException。六、测试验证用数据说话的行为边界该类的行为在 tests/Humanizer.Tests/TruncatorTests.cs 中由四组[Theory]用例覆盖分别对应默认省略号/自定义分隔符 × 右侧/左侧四种组合。从测试数据中可以提炼出以下可复现的边界事实6.1 预算充足或相等时原样返回// 字母/数字总数 47 ≤ 47 Text with number of characters equal to truncate length .Truncate(47, Truncator.DynamicNumberOfCharactersAndPreserveWords) // Text with number of characters equal to truncate length // 字母/数字总数 41 ≤ 41 Text with less characters than truncate length .Truncate(41, Truncator.DynamicNumberOfCharactersAndPreserveWords) // Text with less characters than truncate length测试数据TruncatorTests.cs L71-L72说明只要字母/数字数量不超预算无论字符串总字符数多少都返回原串。6.2 首词放不下时只返回分隔符Textual with delimiter length less than truncate length and starting word longer than truncate length to truncation string alone .Truncate(4, Truncator.DynamicNumberOfCharactersAndPreserveWords) // …用例 TruncatorTests.cs L73 验证了整个前缀只有一个超长单词、没有任何空白可回溯时退化为仅分隔符的行为用...作为分隔符、预算为 4 时同理返回...L161。6.3 连续空格与空分隔符的特殊处理// 预算 10分隔符 null等价于空串前缀 TrimEnd 后保留完整单词 Text with additional spaces and null truncate string .Truncate(10, null, Truncator.DynamicNumberOfCharactersAndPreserveWords) // Text with L158 // 分隔符 且内容放不下 → 返回空串 Text with delimiter length greater than truncate length truncates nothingness without truncation string .Truncate(2, , Truncator.DynamicNumberOfCharactersAndPreserveWords) // L1606.4 分隔符长度与预算的竞争关系// 分隔符 ..... 长度 5预算 4后缀 fit 可放、但分隔符比后缀长且超预算 → 返回后缀本身 A Text with delimiter length less than truncate length and the last word fit .Truncate(4, ....., Truncator.DynamicNumberOfCharactersAndPreserveWords, TruncateFrom.Left) // fit L312 // 预算 5分隔符加 fit 超预算 → 只返回分隔符 .Truncate(5, ....., Truncator.DynamicNumberOfCharactersAndPreserveWords, TruncateFrom.Left) // ..... L313这一组数据TruncatorTests.cs L311-L314完整验证了源码中delimiter.Length suffixLength delimiter.Length totalLength与delimiter.Length suffixLength totalLength两条分支的优先级分隔符本身超出预算时优先保内容而不是保分隔符。6.5 空值与极短输入的约定// null → null空串 → 空串单字符且预算 1 → 原样返回 (null).Truncate(10, Truncator.DynamicNumberOfCharactersAndPreserveWords) // null L67 ().Truncate(10, Truncator.DynamicNumberOfCharactersAndPreserveWords) // L68 (a).Truncate(1, Truncator.DynamicNumberOfCharactersAndPreserveWords) // a L69这些用例TruncatorTests.cs L67-L69与NotNullIfNotNull的空值契约一一对应。七、与其它内置截断器的选型对比Humanizer 在 Truncator.cs 中共暴露五个截断器理解差异有助于正确选型截断器计数单位是否保整词典型场景FixedLength字符串总字符数否硬切严格限制显示宽度如标签、日志FixedNumberOfCharacters字符串总字符数否硬切同上语义等价于前者计数单位相同实现略有差异FixedNumberOfWords单词数量天然整词摘要生成、按句意截断DynamicLengthAndPreserveWords字符串总字符数是回溯空白需要保整词且接受长度略有浮动DynamicNumberOfCharactersAndPreserveWords本文主题字母/数字数量是回溯空白需要保整词且希望空格/符号不占预算与最接近的DynamicLengthAndPreserveWordsTruncatorsrc/Humanizer/Truncation/DynamicLengthAndPreserveWordsTruncator.cs相比两者的回溯策略相同差异只在计数口径前者按总字符数计算预算后者只统计字母与数字。这意味着在包含大量标点、表情符号或宽字符的文本上两者的截断结果会明显不同——例如Text with strange characters ^$(*^ and more ^$**)% 在预算 10、从左侧截断时本类返回…^$(*^ and more ^$**)% TruncatorTests.cs L226符号与空格未被计入预算从而保留了更多内容。八、注意事项与适用前提计数只认字母与数字char.IsLetterOrDigit覆盖字母含中文等非拉丁文字与数字但不覆盖下划线、标点、空白、emoji如果你的场景要求物理字符数严格受限应改用FixedLength或DynamicLengthAndPreserveWords超长单词会触发仅分隔符退化当一个词比整个预算还长且前面没有可回溯的空白时返回结果可能只是分隔符甚至空串这是保整词的代价UI 场景需自行兜底展示分隔符超预算的优先级源码保证分隔符放不下时优先保内容返回后缀/前缀本体而分隔符能放下但内容加分隔符放不下时保分隔符只返回分隔符两条规则均由测试用例锁定可作为行为契约依赖扩展方法入口统一使用默认分隔符…需要...等自定义分隔符时务必走带truncationString参数的重载空值安全value为null时所有重载均返回null可放心用于可空字符串字段。结语DynamicNumberOfCharactersAndPreserveWordsTruncator是 Humanizer 截断体系中语义优先的代表实现以字母/数字为预算单位、以空白为裁切线、以分隔符为兜底在 DynamicNumberOfCharactersAndPreserveWordTruncator.cs 约 220 行代码内完成了从扫描、回溯到预算再校验的完整闭环。配合 TruncateExtensions.cs 的四种重载与 TruncatorTests.cs 中数十条边界用例你可以在摘要生成、面包屑截断、卡片标题裁剪等场景中放心使用并依据测试数据精确预判每一个边界输入的输出结果。赞分享开发工具【免费下载链接】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 字符串截断指南TruncateExtensions 与 ITruncator 深度解析Humanizer 字符串截断指南TruncateExtensions 与 ITruncator 深度解析 TruncateExtensions 是 Huma开发工具Humanizer 字符串截断指南Truncator 类与五种 ITruncator 实现详解Humanizer 字符串截断指南Truncator 类与五种 ITruncator 实现详解 本文聚焦 Humanizer 库中的 Truncator ht开发工具Humanizer 字符串截断方向详解TruncateFrom 枚举的 Left 与 Right 实战指南Humanizer 字符串截断方向详解TruncateFrom 枚举的 Left 与 Right 实战指南 导读 本文围绕 Humanizer 的字符串截断功开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考