求可解析printf格式说明符的Delphi源码以实现多语言格式校验
实现Delphi下printf格式说明符的校验方案
核心思路:基于Delphi RTL源码改造
Delphi的RTL(运行时库)中SysUtils.pas文件里的Format系列函数(如WideFormat)包含完整的printf格式说明符解析逻辑,你可以直接提取改造,无需从零开发。以下是具体实现步骤:
1. 提取并改造格式符解析逻辑
从SysUtils.pas中剥离格式符解析的核心代码,封装成独立的格式符提取函数,能识别完整的格式说明符(包括标志位、宽度、精度、类型字符):
type TFormatSpec = record FullSpec: string; // 完整格式符,如'%10.2f' TypeChar: Char; // 类型标识,如'd'/'s'/'f' end; TFormatSpecList = array of TFormatSpec; function ExtractPrintfFormatSpecs(const AStr: string): TFormatSpecList; var I: Integer; SpecStart: Integer; CurrentSpec: string; begin SetLength(Result, 0); I := 1; while I <= Length(AStr) do begin if AStr[I] = '%' then begin SpecStart := I; Inc(I); // 处理转义的%% if (I <= Length(AStr)) and (AStr[I] = '%') then begin Inc(I); Continue; end; // 跳过标志位、宽度、精度等前缀 while (I <= Length(AStr)) and (AStr[I] in ['-', '+', '0', ' ', '#', '1'..'9', '.']) do Inc(I); // 提取完整格式符(包含类型字符) if (I <= Length(AStr)) and (AStr[I] in ['d', 'i', 'u', 'x', 'X', 'f', 'e', 'E', 'g', 'G', 'c', 's', 'p', 'n']) then begin CurrentSpec := Copy(AStr, SpecStart, I - SpecStart + 1); SetLength(Result, Length(Result) + 1); Result[High(Result)].FullSpec := CurrentSpec; Result[High(Result)].TypeChar := AStr[I]; Inc(I); end else Inc(I); // 无效格式符,直接跳过 end else Inc(I); end; end;
2. 实现原串与翻译串的格式符校验
通过对比两个字符串提取出的格式符列表,判断一致性:
- 先检查列表长度是否一致
- 再逐个对比对应位置的格式符(可选择严格匹配完整规格,或仅匹配类型字符)
function AreFormatSpecsMatching(const ASrcStr, ADestStr: string): Boolean; var SrcSpecs, DestSpecs: TFormatSpecList; I: Integer; begin SrcSpecs := ExtractPrintfFormatSpecs(ASrcStr); DestSpecs := ExtractPrintfFormatSpecs(ADestStr); // 先校验数量是否一致 Result := Length(SrcSpecs) = Length(DestSpecs); if not Result then Exit; // 逐个校验格式符(这里用严格匹配完整规格,若只需类型一致可替换为TypeChar对比) for I := 0 to High(SrcSpecs) do begin if SrcSpecs[I].FullSpec <> DestSpecs[I].FullSpec then begin Result := False; Exit; end; end; end;
3. 适配C++ printf的扩展格式符
如果需要兼容C++特有的格式符(如%lld、%zu),只需扩展类型字符的判断逻辑:
// 修改类型判断部分的代码 if (I <= Length(AStr)) then begin // 处理长整型格式符 if AStr[I] = 'l' then begin Inc(I); if (I <= Length(AStr)) and (AStr[I] in ['d', 'i', 'u', 'x', 'X']) then begin CurrentSpec := Copy(AStr, SpecStart, I - SpecStart + 1); SetLength(Result, Length(Result) + 1); Result[High(Result)].FullSpec := CurrentSpec; Result[High(Result)].TypeChar := AStr[I]; Inc(I); Continue; end; end // 处理size_t类型的%zu else if (AStr[I] = 'z') and (I+1 <= Length(AStr)) and (AStr[I+1] = 'u') then begin CurrentSpec := Copy(AStr, SpecStart, 3); SetLength(Result, Length(Result) + 1); Result[High(Result)].FullSpec := CurrentSpec; Result[High(Result)].TypeChar := 'u'; Inc(I, 2); Continue; end // 标准类型判断 else if AStr[I] in ['d', 'i', 'u', 'x', 'X', 'f', 'e', 'E', 'g', 'G', 'c', 's', 'p', 'n'] then begin CurrentSpec := Copy(AStr, SpecStart, I - SpecStart + 1); SetLength(Result, Length(Result) + 1); Result[High(Result)].FullSpec := CurrentSpec; Result[High(Result)].TypeChar := AStr[I]; Inc(I); end else Inc(I); end;
注意事项
- 不同Delphi版本的RTL逻辑可能略有差异,建议直接查看对应版本
SysUtils.pas的Format实现,确保提取的解析逻辑准确。 - 若需要支持更多C++扩展格式符,可根据printf规范继续扩展类型判断分支。
内容的提问来源于stack exchange,提问作者alancc
相关产品推荐
相关产品推荐

