如何检查NSString格式是否包含与可变参数相同数量的说明符?

问题描述 投票:3回答:6

为了确保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"]会记录

“两件事:你好,”

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

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

objective-c nsstring stringwithformat variadic-functions
6个回答
1
投票

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

不 - 真的不是。至少,如果您希望它跨所有可能的格式字符串工作,那就不是了。您必须复制stringWithFormat:使用的解析器。即不要试图验证一切。

你可以计算%的数量,但这不会像%%或其他特殊情况那样。这可能足以满足您的目的。


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)];

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。您可以通过声明方法(在您的@interface中)告诉编译器参数列表必须是nil终止的,如下所示:

@interface MyObject : NSObject

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

@end

现在,如果编译器调用您的方法而不在参数列表的末尾添加文字nil,编译器将警告用户“在方法调度中丢失了哨兵”。

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

- (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);
}

0
投票

long specifierCount = [myFormatString componentsSeparatedByString:@“%”]。count;

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


0
投票

您可以计算格式说明符的数量,但是IIRC您永远无法计算传递给变量参数方法的参数数量。这是因为C推送堆栈上的参数而不指定推送的数量。

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


0
投票

您可以在函数原型的末尾使用NS_FORMAT_FUNCTION,如stringWithFormatNSString方法。

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

- (void)thingsForStuff:(CustomStuff)stuff, ... NS_FORMAT_FUNCTION(1,2);
© www.soinside.com 2019 - 2024. All rights reserved.