解析 Objective-C 的 printf 格式字符串?

10
我想要获得使用 +[NSString stringWithFormat:] 创建的 NSString 成员范围。如何解析 Objective-C 格式字符串是最佳方法?我不能只使用 C 格式字符串解析器,因为它无法处理 %@。我还需要确保它支持格式排序: %1$d%2$@ 等。

举例来说,对于用 [NSString stringWithFormat:@"foo %2$@ bar %1$@", @"Heath", @"Borders"] 创建的字符串,我希望得到以下 NSArray: @[NSMakeRange(15, 5), NSMakeRange(4, 6)]。第一个数组对象对应于格式字符串中的第一个数据元素,第二个数组对象对应于第二个数据元素,以此类推。

在这种情况下,API 看起来像 + (NSString *)stringWithFormatRanges:(NSArray **)outFormatRanges withFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); 它将返回与 +[NSString stringWithFormat:] 类似的 NSString,但它还将返回一个包含每个格式数据元素的 NSRangeNSArray

-- 编辑 --

鉴于这个问题已经三年了,目前我会接受仅 C 的实现。

3
“Ranges of members”是什么意思?你是说你想解析诸如“%@”、“%1$d”和“%2$@”等子字符串吗? - Extra Savoir-Faire
1
尽管没有记录,但 C printf()%n specifier 在 Objective-C 中是否按预期工作?如果是这样的话,编写一个围绕 stringWithFormat 的包装器,立即在其他 specifiers 之前和之后注入 %n,调用格式化程序,然后将字节计数后处理为所需的任何形式就像编写简单的代码一样。 - Iwillnotexist Idonotexist
我将不存在,我不存在,这非常方便!我不知道%n!然而,“编写一个围绕stringWithFormat的包装器,在其他说明符之前和之后立即注入%n”似乎一点也不简单。说明符语法非常复杂!:( - Heath Borders
@HeathBorders 嗯,我有点保留:我不确定 %n 是否适用于 stringWithFormat,而“大约简单”只是对复杂性的上限进行了界定。但我认为,用 %n 包装说明符可能会比实现自己的格式化打印机或在打印后对字符串进行疯狂分析要稍微容易一些。此外,解析规范是否就像扫描未转义的 %,然后扫描规范的下一个“终止”字母之一,其中包括 @diuoxXeEfFgGaAcspn?在两者之间,您可以使用其他字符,但据我所知不能使用那些字符。 - Iwillnotexist Idonotexist
2
@HeathBorders 嗯,C标准对printf()的描述使用了一组不相交的字符来表示转换说明符和介于其间的内容(_标志、字段宽度、精度、长度修饰符_)。让我困扰的是%@转换说明符可能不被普通的printf()支持,但%n可能不被stringWithFormat支持。 - Iwillnotexist Idonotexist
显示剩余6条评论
2个回答

5
我几年前就研究过这个问题。如果我理解你的问题,无论是C还是Cocoa格式字符串,都没有简单的方法来获取AST。
我编写了NSXMLElement+elementWithXMLFormat,它允许在格式字符串中插入未转义的NSXMLElements,并使用特殊的%%%@格式代码扩展了Cocoa的格式。我的技术可能对你有帮助。

https://github.com/rentzsch/nsxmlelement-elementwithxmlformat/blob/937b54b2a830a8fbbd72d6bc5e48bafd495ddcbd/NSXMLElement%2BelementWithXMLFormat.m#L41

我提取并使用NUL分隔格式代码,传统地运行它,然后重新组合。

谢谢!我今晚会看一下。它看起来很有前途。 - Heath Borders

4

我用Swift编写了一个解析器(这样我就可以检测Localizable.strings中的占位符,并提取在调用stringWithFormat:时预期给定的类型),为了我的工具SwiftGen

您可以在我的GitHub上看到执行解析的代码那里

  • 当然,它是用Swift而不是Objective-C编写的,但我想只要保留算法的逻辑,它很容易转换为ObjC。
  • 我的代码也没有将范围作为这些函数的输出返回 - 只返回类型 - 但是typesFromFormatString函数中的代码绝对可以访问这些占位符的范围,并且完全可以将它们与检测到的类型一起返回(这很容易更改,我只是不需要这些范围,因此没有将它们包含在返回类型中)
一旦您获得了像%d%2$@这样的占位符范围,就很容易推断出字符串参数的范围:第一个范围将具有与第一个占位符相同的.location,但替换值的长度,第二个范围将具有第二个占位符的.location+(firstValue.length-firstRange.length)以考虑偏移量,等等。希望对您有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接