如何创建一个NSString从一种格式串像@"xxx=%@,yyy=%@",一个是的对象?
-
21-08-2019 - |
题
是否有任何方法来创建一个新的 NSString从一种格式串像@"xxx=%@,yyy=%@",一个是的对象?
在NSSTring类的方法有很多,如:
- (id)initWithFormat:(NSString *)format arguments:(va_list)argList
- (id)initWithFormat:(NSString *)format locale:(id)locale arguments:(va_list)argList
+ (id)stringWithFormat:(NSString *)format, ...
但不是他们需要一个是作为参数,和我无法找到一种方式来创建一个va_list从一个是...
解决方案
它实际上是不努力创造一个va_list从一个是.见马特*加拉格尔的 优秀的文章 该问题。
这里是一个NSString类别做你想要什么:
@interface NSString (NSArrayFormatExtension)
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments;
@end
@implementation NSString (NSArrayFormatExtension)
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments
{
char *argList = (char *)malloc(sizeof(NSString *) * arguments.count);
[arguments getObjects:(id *)argList];
NSString* result = [[[NSString alloc] initWithFormat:format arguments:argList] autorelease];
free(argList);
return result;
}
@end
然后:
NSString* s = [NSString stringWithFormat:@"xxx=%@, yyy=%@" array:@[@"XXX", @"YYY"]];
NSLog( @"%@", s );
不幸的是,64位,动作列表格式已经改变,因此上述代码不再工作。并且大概不应被用无论如何给它取决于格式,显然是受到改变。鉴于没有真正可靠的方法,以创建一个va_list,一个更好的解决方案是简单的数量限制的辩论,以一个合理的最大(说10)和然后打电话接与第一次10个参数,事情是这样的:
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments
{
if ( arguments.count > 10 ) {
@throw [NSException exceptionWithName:NSRangeException reason:@"Maximum of 10 arguments allowed" userInfo:@{@"collection": arguments}];
}
NSArray* a = [arguments arrayByAddingObjectsFromArray:@[@"X",@"X",@"X",@"X",@"X",@"X",@"X",@"X",@"X",@"X"]];
return [NSString stringWithFormat:format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9] ];
}
其他提示
在此基础上回答使用自动参考计数(ARC): https://stackoverflow.com/a/8217755/881197
添加一个类别, NSString
用以下方法:
+ (id)stringWithFormat:(NSString *)format array:(NSArray *)arguments
{
NSRange range = NSMakeRange(0, [arguments count]);
NSMutableData *data = [NSMutableData dataWithLength:sizeof(id) * [arguments count]];
[arguments getObjects:(__unsafe_unretained id *)data.mutableBytes range:range];
NSString *result = [[NSString alloc] initWithFormat:format arguments:data.mutableBytes];
return result;
}
一个解决方案,来到我的头脑我就可以创建一个方法工作的一个固定的大量参数,如:
+ (NSString *) stringWithFormat: (NSString *) format arguments: (NSArray *) arguments {
return [NSString stringWithFormat: format ,
(arguments.count>0) ? [arguments objectAtIndex: 0]: nil,
(arguments.count>1) ? [arguments objectAtIndex: 1]: nil,
(arguments.count>2) ? [arguments objectAtIndex: 2]: nil,
...
(arguments.count>20) ? [arguments objectAtIndex: 20]: nil];
}
我还可以添加一检查,看如果格式串已超过21'%'字并把一个异常,在这种情况。
@Chuck 是正确的有关事实的你 不能换一个是成varargs.然而,我不建议在寻找模式 %@
字符串中和替换,它每次.(替换人物中间的串通常是相当低效率的,并不是一个好主意如果你可以完成同样的事情在不同的方式。) 这里是一个更有效的方式来创建一个字符格式,你说的是:
NSArray *array = ...
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array) {
[newArray addObject:[NSString stringWithFormat:@"x=%@", [object description]]];
}
NSString *composedString = [[newArray componentsJoinedByString:@", "] retain];
[pool drain];
包括我自动释放游泳池好的客房服务,因为一个串将创建对各列项,而可变阵列是个如此。你可以容易地做到这一方法/多功能和返回 composedString
没有保留和处理的自动释放其他地方代码,如果所希望的。
这答案是越野车。正如所指出的,没有解决这个问题,是保证工作时,新的平台推出比其他使用"10元件阵列"的方法。
答案通过solidsun运作良好,直到我去汇编有64位的建筑。这引起了一个错误:
EXC_BAD_ADDRESS类型EXC_I386_GPFLT
解决的办法是使用一个稍微不同的方法传递的参数列表的方法:
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments;
{
__unsafe_unretained id * argList = (__unsafe_unretained id *) calloc(1UL, sizeof(id) * arguments.count);
for (NSInteger i = 0; i < arguments.count; i++) {
argList[i] = arguments[i];
}
NSString* result = [[NSString alloc] initWithFormat:format, *argList] ;// arguments:(void *) argList];
free (argList);
return result;
}
这只是工作组与一个单一的元素
还有 是没有一般的方式 通过一系列函数或方法的使用varargs.在这种特定情况下,然而,可以假的使用是这样的:
for (NSString *currentReplacement in array)
[string stringByReplacingCharactersInRange:[string rangeOfString:@"%@"]
withString:currentReplacement];
编辑:所接受的答案要求有一种方法来这样做,但无论多么脆弱,这个答复似乎是,这种做法是更加脆弱。它依赖于执行定义的行为(具体地说,结构 va_list
)是不保证保持相同。我认为,我的答案是正确的,我的提议的解决方案是少脆弱,因为它只能依靠定义特征的语言和框架。
对于那些需要迅速解决,这是一个扩展到这样做在迅速
extension String {
static func stringWithFormat(format: String, argumentsArray: Array<AnyObject>) -> String {
let arguments = argumentsArray.map { $0 as! CVarArgType }
let result = String(format:format, arguments:arguments)
return result
}
}
是的,这是可能的。在海湾合作委员会针对Mac OS X,至少, va_list
是一个简单的C阵列,所以你会让一个 id
s,然后告诉的是填补:
NSArray *argsArray = [[NSProcessInfo processInfo] arguments];
va_list args = malloc(sizeof(id) * [argsArray count]);
NSAssert1(args != nil, @"Couldn't allocate array for %u arguments", [argsArray count]);
[argsArray getObjects:(id *)args];
//Example: NSLogv is the version of NSLog that takes a va_list instead of separate arguments.
NSString *formatSpecifier = @"\n%@";
NSString *format = [@"Arguments:" stringByAppendingString:[formatSpecifier stringByPaddingToLength:[argsArray count] * 3U withString:formatSpecifier startingAtIndex:0U]];
NSLogv(format, args);
free(args);
你不应该依靠这种性质在代码,应该便携式。iPhone的开发,这是一件事,你绝对应该试验设备。
- (NSString *)stringWithFormat:(NSString *)format andArguments:(NSArray *)arguments {
NSMutableString *result = [NSMutableString new];
NSArray *components = format ? [format componentsSeparatedByString:@"%@"] : @[@""];
NSUInteger argumentsCount = [arguments count];
NSUInteger componentsCount = [components count] - 1;
NSUInteger iterationCount = argumentsCount < componentsCount ? argumentsCount : componentsCount;
for (NSUInteger i = 0; i < iterationCount; i++) {
[result appendFormat:@"%@%@", components[i], arguments[i]];
}
[result appendString:[components lastObject]];
return iterationCount == 0 ? [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] : result;
}
测试格式和参数:
NSString *format = @"xxx=%@, yyy=%@ last component";
NSArray *arguments = @[@"XXX", @"YYY", @"ZZZ"];
结果是:xxx XXX,yyy=YYY最后一部分
NSString *format = @"xxx=%@, yyy=%@ last component";
NSArray *arguments = @[@"XXX", @"YYY"];
结果是:xxx XXX,yyy=YYY最后一部分
NSString *format = @"xxx=%@, yyy=%@ last component";
NSArray *arguments = @[@"XXX"];
结果是:xxx XXX最后一部分
NSString *format = @"xxx=%@, yyy=%@ last component";
NSArray *arguments = @[];
结果是:最后一部分
NSString *format = @"some text";
NSArray *arguments = @[@"XXX", @"YYY", @"ZZZ"];
结果是:一些文字
我找到一些代码在网声称,这是可能的,但是我还没有做到它自己,但是如果你不知道这些参数提前你还需要建立的格式串的动态,所以我只是不明白这一点。
你最好只是建立的串通过迭代的阵列。
你可能会找到stringByAppendingString:或stringByAppendingFormat:实例的方法方便。
一个可以创建一个类别NSString和使功能,这接收到的格式,列和返回的串与替换为对象。
@interface NSString (NSArrayFormat)
+ (NSString *)stringWithFormat:(NSString *)format arrayArguments:(NSArray *)arrayArguments;
@end
@implementation NSString (NSArrayFormat)
+ (NSString *)stringWithFormat:(NSString *)format arrayArguments:(NSArray *)arrayArguments {
static NSString *objectSpecifier = @"%@"; // static is redundant because compiler will optimize this string to have same address
NSMutableString *string = [[NSMutableString alloc] init]; // here we'll create the string
NSRange searchRange = NSMakeRange(0, [format length]);
NSRange rangeOfPlaceholder = NSMakeRange(NSNotFound, 0); // variables are declared here because they're needed for NSAsserts
NSUInteger index;
for (index = 0; index < [arrayArguments count]; ++index) {
rangeOfPlaceholder = [format rangeOfString:objectSpecifier options:0 range:searchRange]; // find next object specifier
if (rangeOfPlaceholder.location != NSNotFound) { // if we found one
NSRange substringRange = NSMakeRange(searchRange.location, rangeOfPlaceholder.location - searchRange.location);
NSString *formatSubstring = [format substringWithRange:substringRange];
[string appendString:formatSubstring]; // copy the format from previous specifier up to this one
NSObject *object = [arrayArguments objectAtIndex:index];
NSString *objectDescription = [object description]; // convert object into string
[string appendString:objectDescription];
searchRange.location = rangeOfPlaceholder.location + [objectSpecifier length]; // update the search range in order to minimize search
searchRange.length = [format length] - searchRange.location;
} else {
break;
}
}
if (rangeOfPlaceholder.location != NSNotFound) { // we need to check if format still specifiers
rangeOfPlaceholder = [format rangeOfString:@"%@" options:0 range:searchRange];
}
NSAssert(rangeOfPlaceholder.location == NSNotFound, @"arrayArguments doesn't have enough objects to fill specified format");
NSAssert(index == [arrayArguments count], @"Objects starting with index %lu from arrayArguments have been ignored because there aren't enough object specifiers!", index);
return string;
}
@end
因为是创建在运行时,我们不能提供编制时警告,但我们可以使用NSAssert告诉我们,如果数量的说明等于数量目的内阵列。
创建了一个 项目 在审查在这个类别中都可以找到。还增加了Chuck的版本通过使用'stringByReplacingCharactersInRange:',加上一些测试。
利用一个万物体进入阵列,版本与'stringByReplacingCharactersInRange:'不规模很好(等待了大约2分钟然后关闭的应用程序).使用该版本与NSMutableString、功能做串大约4秒钟。该试验是使用模拟器。 使用前,测试应该可以做一个真正的设备(使用设备的最低specs).
编辑:在iPhone上5s的版本与NSMutableString需要10.471655s(一百万对象);在iPhone上5需要21.304876s.
这里的答案没有明确地建立一个阵列:
NSString *formattedString = [NSString stringWithFormat:@"%@ World, Nice %@", @"Hello", @"Day"];
第一串是目标字符格式,下一串串待插入的目标。
不,你不能。变量的参数呼吁都解决了在编制时间,和你是有内容只能在运行时间。