【Hi.Ltd 专题】第3期·中:Utilities 字符串扩展(工控配置与报文文本)
Hi.Ltd 专题第 3 期·中|模块:Utilities(字符串扩展)|NuGet:
Hi.Ltd2026.7.11.1437
目标框架:net462/net481/net6.0/net8.0
场景:配方名校验、INI/路径键、PLC IP、报文 ASCII/Hex 文本、空串与空白串拦截。
第3期·上:Utilities 数值类型扩展(工控寄存器与限幅换算)
本期目标
把上位机配置层与报文层里「空串、非法 IP、非数字、Hex 文本、截取段、TitleCase 显示」统一到 Hi.Ltd.Utilities 字符串扩展,返回值多数为 Result<T>。
读完你应能:
- 用
IsNullOrEmpty/IsNullOrWhiteSpace/IsNotNullOrEmpty做配置与 HMI 输入门禁 - 用
IsIpAddressValid/IsNumberValid/IsInt32/IsPathValid/IsValidEmail等做字段级校验 - 用
HexToBytes/ToHexString/GenHexString/GetBytes/GetASCCIIChars做报文文本 ↔ 二进制 - 用
Split/SplitFirst/SplitLast/Substring/TrimChars/Replace/Remove*解析配方名与键路径 - 分清 README 快速入门里的
ToBase64简化名与真实 APIToBase64String/FromBase64String
适用范围
| 场景 | API 方向 |
|---|---|
| 配方名、工单号、批次号非空 | IsNullOrWhiteSpace / IsNotNullOrEmpty |
| PLC IP、MES URL 主机段 | IsIpAddressValid / IsMatch |
| INI 里速度/温度字符串转数 | IsInt32/IsDouble → ToInt32/ToDouble |
| 抓包日志:字节 ↔ Hex 文本 | ToHexString / HexToBytes / GenHexString |
| 设备名显示规范化 | ToTitleCase / TrimChars / ToSnakeCase |
| 路径合法性(导出目录) | IsPathValid / GetDirectories / GetFiles |
不该用 / 慎用
| 场景 | 说明 |
|---|---|
| 把 JSON/YAML/INI 完整业务模型反序列化当「字符串小工具」用完就走 | FromJson/FromYaml/FromIni 等在 Utilities 上存在,系统讲解见下篇;复杂契约建议放到 Interop 期 |
热路径每包 ToHexString 打全帧 | Hex 日志放抽样/失败分支 |
正则 IsMatch 写灾难回溯 | 工控输入应白名单 + 长度上限 |
| 文化/多语言资源 | CultureScope / GetResourceString 见下篇 |
关键约定
| 约定 | DLL 实测 |
|---|---|
校验类 Is* | 多数 → Result<bool>(另有 *Unsafe → bool) |
变换类 To* / Split* / Trim* | 多数 → Result<string> 或 Result<T> |
NewGuid() | → string(非 Result) |
GetCombinations | → IEnumerable<string> |
TrimToNull / ToSnakeCase | → string(裸) |
FromJson | → T(泛型,失败行为以实现为准;系统讲见下篇) |
另有 Result<string> 入参重载 | 与上期桥接 |
分组详解
1. 空串 / 空白 / 相等:门禁组
| 方法 | 典型入参 | 返回 | 说明 |
|---|---|---|---|
IsNullOrEmpty | string / 数组 / Guid / 字典… | Result<bool> | 空或 null |
IsNullOrWhiteSpace | string / Result<string> | Result<bool> | null/空/纯空白 |
IsNullOrWhiteSpaceEx | 字符串族 | 见 DLL | 扩展变体 |
IsNotNullOrEmpty | 同族 | Result<bool> | 正向门禁 |
IsEqual / IsNotEqual | 字符串等 | Result<bool> | 相等比较 |
Contains / StartsWith / EndsWith | string 或 Result<string> | Result<bool> | 片段判断 |
using Hi.Ltd;
string recipeName = hmi.RecipeText;
if (recipeName.IsNullOrWhiteSpace().Content)
return Error.Empty(nameof(recipeName), "配方名不能为空");
string a = "LINE-A";
string b = "line-a";
// 是否大小写敏感以具体重载为准;不确定就先规范化再比
Result<bool> same = a.IsEqual(b);
工控:下载配方前、写 INI 前、连 PLC 前,三位门禁一起做。
2. 格式校验:IP / 数字 / 路径 / 邮件 / 日期 / 语言字符
| 方法 | 返回 | 工控用途 |
|---|---|---|
IsIpAddressValid | Result<bool> | PLC/相机 IP |
IsNumberValid | Result<bool> | 是否整数串 |
IsValidRealNumber / IsValidUNumber | Result<bool> | 实数 / 无符号数 |
IsPathValid | Result<bool> | 导出路径、配方目录 |
IsValidEmail | Result<bool> | 报警推送邮箱 |
IsValidDateFormat | Result<bool> | 批次日期串 |
IsGuid | Result<bool> | 工单 GUID |
IsEnglish / IsChineseText | Result<bool> | 语言策略 |
IsContainsChinese / IsContainsChineseCharacter | Result<bool> | 是否含中文 |
IsContainsSpecialChars | Result<bool> | 特殊字符拦截(文件名) |
IsMatch / IsNotMatch | Result<bool> | 正则(慎用) |
类型探测(字符串是否可解析为某 CLR 类型):
IsBoolean / IsByte / IsChar / IsDecimal / IsDateTime / IsDouble / IsSingle /
IsInt16 / IsInt32 / IsInt64 / IsSByte / IsUInt16 / IsUInt32 / IsUInt64
全部安全版 → Result<bool>。
using Hi.Ltd;
string ip = "192.168.1.10";
if (!ip.IsIpAddressValid().Content)
return Error.Format(nameof(ip), "PLC IP 非法");
string speed = ini["Axis","Speed"]; // 示意
if (!speed.IsInt32().Content)
return Error.Format("Speed", "速度必须是整数");
Result<int> rpm = speed.ToInt32();
3. 字符串 → 数值 / 字节 / 布尔 / 时间
| 方法 | 返回(DLL) | 说明 |
|---|---|---|
ToInt16/ToInt32/ToInt64/ToUInt*/ToByte/ToSByte | Result<T> | 配置转寄存器值 |
ToDouble/ToSingle/ToDecimal | Result<T> | 工程量 |
ToBoolean/ToBooleans | Result<bool>… | "true"/开关 |
ToDateTime | Result<DateTime> | 时间串 |
ToIPAddress | Result<IPAddress> | 连板用 |
GetBytes(this string) / GetBytes(string, Encoding) | Result<byte[]> | 默认 UTF-8(README) |
ToBytes / ToByte | Result<byte[]> / Result<byte> | 近义族 |
GetAsciiBytes | Result<byte[]> | ASCII 报文 |
HexToBytes | Result<byte[]> | Hex 文本 → 字节 |
FromBase64String | Result<byte[]> | Base64 → 字节 |
空串 → Error.Empty;格式错 → Error.Format(README 对 Hex/Base64/部分转换的描述)。
using System.Text;
using Hi.Ltd;
// 报文 ASCII
Result<byte[]> ascii = "RD D100".GetBytes(Encoding.ASCII);
// 抓包窗口粘贴的 Hex
Result<byte[]> frame = "01030FA00001".HexToBytes();
if (!frame.Successed) return frame; // Result 桥
// Base64 配方备份片段
Result<byte[]> blob = backupB64.FromBase64String();
README 快速入门写过
ToBase64()/FromBase64()——包内公开名为ToBase64String/FromBase64String(DLL 已核对)。
4. 数值/字节 → 字符串:显示与日志
| 方法 | 入参侧 | 返回 | 说明 |
|---|---|---|---|
ToHexString(this byte/short/int/…, bool) | 数值 | Result<string> | 单值 Hex |
GenHexString(this byte[]) | 字节数组 | Result<string> | 缓冲区 Hex 文本 |
ToBase64String(this byte[] …) | 字节 | Result<string>(部分重载见 DLL) | Base64 |
FormatWithDecimal(this long, int) | 整数 | Result<string> | 固定小数显示 |
ToSaveString | 字符串等 | Result<string> | 可落盘友好串 |
ToTitleCase | 字符串 | Result<string> | 标题格式 |
ToSnakeCase | 字符串 | string | snake_case |
ToStringBuilder | 字符串 | Result<StringBuilder> | 可变拼接 |
GetString(this byte[] …) | 字节 | Result<string> | 解码文本 |
NewGuid() | 无 this 或静态扩展形态 | string | 新 GUID 字符串 |
using Hi.Ltd;
byte[] pdu = frame.Content;
Result<string> hexLog = pdu.GenHexString();
plc.Debug("TX " + hexLog.Content);
long raw = 12345; // 0.001 工程单位
Result<string> show = raw.FormatWithDecimal(3); // 显示层
5. 切割 / 替换 / 子串 / 查找
| 方法 | 返回 | 说明 |
|---|---|---|
Split | Result<string> | 按分隔规则拆(重载多,详见 XML) |
SplitFirst / SplitLast | Result<string> | 只取首/尾段 |
Substring | Result<string> | 安全子串 |
Replace | Result<string> | 替换 |
RemoveLast / RemoveOne | Result / Result<string> | 删末段/一处 |
TrimChars | Result<string> | 按给定字符修剪 |
TrimToNull | string | 空白→null 风格 |
IndexOf / LastIndexOf | Result<int> | 查找 |
GetDigit / GetDigits | Result<int> / 数组 | 从字符串抽数字字符 |
using Hi.Ltd;
// 配方键:Line1/Recipe/Speed
string key = "Line1/Recipe/Speed";
Result<string> head = key.SplitFirst("/"); // Line1
Result<string> tail = key.SplitLast("/"); // Speed
string file = "batch_001.csv";
Result<string> name = file.RemoveLast("."); // 视重载语义去掉末段
6. 路径 / 文件枚举 / 描述 / 组合
| 方法 | 返回 | 说明 |
|---|---|---|
GetDirectories | Result<string[]> | 子目录 |
GetFiles / GetFile | Result<string[]> | 文件列举 |
GetDescription | Result<string> | 描述信息(含枚举描述见下篇交叉) |
GetCombinations | IEnumerable<string> | 组合生成(测试用例/许可键空间慎用) |
FindChineseCharacters | Result<Dictionary<int,string>> | 找出中文位置 |
using Hi.Ltd;
string root = @"D:\Recipes";
if (!root.IsPathValid().Content)
return Error.Format(nameof(root), "配方根路径非法");
Result<string[]> files = root.GetFiles();
7. 与 Result<string> 的桥(点到为止)
几乎所有字符串 API 都有 this Result<string> 重载:通讯函数已经返回 Result<string> 时,可直接:
Result<string> msg = ReadAsciiReply();
Result<bool> okIp = msg.IsIpAddressValid();
Result<string> trimmed = msg.TrimChars(' ', '\r', '\n');
深层 Then/Match/BranchTrace 见第2期,不在此展开。
完整工控示例:配方名 + INI 速度 + Hex 下发
using System.Text;
using Hi.Ltd;
public static class RecipeTextPipeline
{
public static Result<byte[]> BuildWriteFrame(string recipeName, string speedText, string plcIp)
{
if (recipeName.IsNullOrWhiteSpace().Content)
return Error.Empty(nameof(recipeName), "配方名空");
if (recipeName.IsContainsSpecialChars().Content)
return Error.Format(nameof(recipeName), "配方名含特殊字符");
if (!plcIp.IsIpAddressValid().Content)
return Error.Format(nameof(plcIp), "PLC IP 非法");
if (!speedText.IsInt32().Content)
return Error.Format(nameof(speedText), "速度不是整数");
Result<int> speed = speedText.ToInt32();
if (!speed.Successed) return Error.Result(speed);
Result<int> limited = speed.Content.Clamp(0, 3000); // 上篇数值
if (!limited.Successed) return Error.Result(limited);
// 示意:ASCII 命令 + 速度字
string cmd = $"WR SPEED {limited.Content}";
Result<byte[]> body = cmd.GetBytes(Encoding.ASCII);
return body;
}
public static Result LogTx(byte[] pdu)
{
Result<string> hex = pdu.GenHexString();
if (!hex.Successed) return hex;
// Logging:pdu.Debug 不存在;用上期 Logger 扩展
return Result.Success;
}
}
现场坑
- 空串与空白串:只判
IsNullOrEmpty挡不住" "——HMI 输入用IsNullOrWhiteSpace。 IsInt32为 true 仍可能ToInt32失败? 以 DLL 行为为准;生产仍要看Successed。- Hex 串带空格/换行:先
TrimChars/Replace再HexToBytes。 - 编码:
GetBytes()默认 UTF-8;PLC ASCII 协议请显式Encoding.ASCII。 - README
ToBase64简化名:代码里用ToBase64String/FromBase64String。 NewGuid返回 string:不要当Result解包。- 中文路径 / 中文配方:用
IsContainsChinese*做策略,而不是一刀切拒绝(看产线规范)。
异常点与应对
| 现象 | 可能原因 | 应对 |
|---|---|---|
Error.Empty | 空串进 Hex/Base64/GetBytes | 门禁前置 |
Error.Format | Hex 奇数长度、非十六进制字符 | 清洗输入;UI 限制字符集 |
IsIpAddressValid 过宽/过严 | IPv6/端口混在串里 | IP 与端口拆开校验 |
| 路径 API 失败 | 权限/盘符不存在 | 与 IO 期 FileUtils 配合 |
| 正则超时 | IsMatch 灾难表达式 | 改白名单 |
FAQ
Q: 字符串转 int 用 ToInt32 还是 int.Parse?
A: 工控边界推荐 ToInt32→Result,和全库失败模型一致。
Q: GenHexString 和 ToHexString 怎么选?
A: 单数值调试用 ToHexString;整帧缓冲用 GenHexString。
Q: 序列化 FromJson 为何本篇不展开?
A: 属于 Utilities「其他/序列化」能力;Interop 期还会从模块视角再讲。
下篇预告
更多推荐


所有评论(0)