4

为了确保返回的格式化字符串NSString initWithFormat:arguments:符合预期,我需要确定格式说明符的数量是否与参数相同。下面是一个(略微做作和高度编辑的)示例:

- (void)thingsForStuff:(CustomStuff)stuff, ...
{
    NSString *format;
    switch (stuff)
    {
        case CustomStuffTwo:
            format = @"Two things: %@ and %@";
        break;

        case CustomStuffThree:
            format = @"Three things: %@, %@, and %@";
        break;

        default:
            format = @"Just one thing: %@";
        break;
    }

    va_list args;
    va_start(args, method);
    // Want to check if format has the same number of %@s as there are args, but not sure how
    NSString *formattedStuff = [[NSString alloc] initWithFormat:format arguments:args];
    va_end(args);

    NSLog(@"Things: %@", formattedStuff);
}

使用此方法,[self thingsForStuff:CustomStuffTwo, @"Hello", @"World"]将记录

“两件事:你好和世界”

...但[self thingsForStuff:CustomStuffTwo, @"Hello"]会记录

“两件事:你好和”

......在它发生之前最好被抓住的东西。

有没有办法计算字符串中的格式说明符,最好是轻量级/便宜的?

4

6 回答 6

5

好吧,我创建了自己的正则表达式,我不知道它是否会捕获所有这些,并且可能最终会发现一些误报,但似乎对我有用:

static NSString *const kStringFormatSpecifiers =
@"%(?:\\d+\\$)?[+-]?(?:[lh]{0,2})(?:[qLztj])?(?:[ 0]|'.{1})?\\d*(?:\\.\\d+)?[@dDiuUxXoOfeEgGcCsSpaAFn]";

您可以使用以下方法计算参数的数量:

NSRegularExpression *regEx = [NSRegularExpression regularExpressionWithPattern: kStringFormatSpecifiers options:0 error:nil];
NSInteger numSpecifiers = [regEx numberOfMatchesInString: yourString options:0 range:NSMakeRange(0, yourString.length)];
于 2015-04-13T08:16:41.420 回答
2

有没有办法计算字符串中的格式说明符,最好是轻量级/便宜的?

不——真的不是。至少,如果您希望它适用于所有可能的格式字符串,则不是。您将不得不复制stringWithFormat:. 即不要试图验证一切。

您可以计算 的数量%,但这不会捕获诸如%%或其他特殊情况之类的东西。这对于您的目的可能已经足够了。

于 2012-08-09T01:03:36.783 回答
2

由于 C 和 Objective-C 处理像您这样的可变参数函数/方法的方式,您通常无法判断用户提供了多少参数。

这里有两种方法来处理你的情况。

首先,寻找另一种方法来做到这一点。传递给方法的参数数量是在编译时确定的。因此,也许您应该只使用三种方法,而不是使用可变参数方法:

- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing;
- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing1 thing:(Thing *)thing2;
- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing1 thing:(Thing *)thing2 hatWearer:(Cat *)cat;

您可以根据要传递的参数数量选择正确的方法在编译时调用,从而完全消除 switch 语句。

其次,我看到您预定义的格式字符串仅使用该%@格式。这是否意味着您希望用户只将对象传递给您的方法(除了(CustomStuff)stuff参数)?

如果用户只将对象传递给您的方法,并且您要求这些参数不为零,那么您可以让编译器帮助您。更改您的方法以要求用户nil在参数列表的末尾传递。您可以通过声明方法(在您的 )中告诉编译器参数列表必须以 nil 终止,@interface如下所示:

@interface MyObject : NSObject

- (void)thingsForStuff:(CustomStuff)stuff, ... NS_REQUIRES_NIL_TERMINATION

@end

现在,如果用户调用您的方法时没有在参数列表末尾放置文字 nil,编译器将警告用户“在方法调度中缺少标记”。

因此,将您的 API 更改为需要一些非零参数后跟一个零参数,您可以更改方法来计算非零参数,如下所示:

- (void)thingsForStuff:(CustomStuff)stuff, ... {
    int argCount = 0;
    va_list args;
    va_start(args, stuff);
    while (va_arg(args, id)) {
        ++argCount;
    }
    va_end(args)

    int expectedArgCount;
    NSString *format;
    switch (stuff) {
        case CustomStuffTwo:
            expectedArgCount = 2;
            format = @"Two things: %@ and %@";
            break;

        case CustomStuffThree:
            expectedArgCount = 3;
            format = @"Three things: %@, %@, and %@";
            break;

        // etc.
    }

    NSAssert(argCount == expectedArgCount, @"%@ %s called with %d non-nil arguments, but I expected %d", self, (char*)_cmd, argCount, expectedArgCount);

    va_start(args, stuff);
    NSString *formattedStuff = [[NSString alloc] initWithFormat:format arguments:args];
    va_end(args);

    NSLog(@"Things: %@", formattedString);
}
于 2012-08-09T03:20:20.813 回答
0

您可以计算格式说明符的数量,但 IIRC 您将永远无法计算传递给变量参数方法的参数数量。这是因为 C 将参数推送到堆栈上的方式没有指定它已经推送了多少。

大多数函数通过要求最后一个参数为 nil 或某种终止符来克服这个问题(参见 [NSArray arrayWithObjects:])。甚至还有一个宏允许编译器检查这一点并在编译时发出警告。

于 2012-08-09T02:42:21.167 回答
0

您可以NS_FORMAT_FUNCTION在函数原型的末尾使用,stringWithFormatNSString.

所以你的方法的原型应该是这样的:

- (void)thingsForStuff:(CustomStuff)stuff, ... NS_FORMAT_FUNCTION(1,2);
于 2016-07-04T14:46:35.697 回答
-1

long specifierCount = [myFormatString componentsSeparatedByString:@"%"].count;

这会让你接近。它只是一个简单的拆分。您必须考虑转义的 % 值。

于 2012-08-09T01:03:29.257 回答