@“xxx =%@, yyy =%@”및 객체의 nsarray와 같은 형식 문자열에서 nsstring을 만드는 방법?

StackOverflow https://stackoverflow.com/questions/1058736

문제

@"xxx =%@, yyy =%@"및 객체의 nsarray와 같은 형식 문자열에서 새 nsstring을 만들 수있는 방법이 있습니까?

NSString 클래스에는 다음과 같은 많은 방법이 있습니다.

- (id)initWithFormat:(NSString *)format arguments:(va_list)argList
- (id)initWithFormat:(NSString *)format locale:(id)locale arguments:(va_list)argList
+ (id)stringWithFormat:(NSString *)format, ...

그러나 그들 중 누구도 nsarray를 논쟁으로 받아들이지 않으며, 나는 nsarray에서 va_list를 만들 수있는 방법을 찾을 수 없습니다 ...

도움이 되었습니까?

해결책

실제로 nsarray에서 va_list를 만드는 것은 어렵지 않습니다. Matt Gallagher 's를 참조하십시오 훌륭한 기사 주제에.

다음은 원하는 작업을 수행하기위한 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 형식이 변경되었으므로 위의 코드가 더 이상 작동하지 않습니다. 그리고 어쨌든 변경 될 수있는 형식에 따라 다르면 어쨌든 사용해서는 안됩니다. VA_LIST를 만들 수있는 강력한 방법이 없기 때문에 더 나은 솔루션은 인수 수를 합리적인 최대 값으로 제한 한 다음 다음과 같은 첫 10 개의 인수로 StringWithFormat을 호출하는 것입니다.

+ (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 '%'문자가 있는지 확인하고 해당 경우에 예외를 던지기 위해 확인을 추가 할 수 있습니다.

@척 당신이 사실에 대해 맞습니다 nsarray를 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) 그것은 동일하게 유지되는 것을 보장하지 않습니다. 나는 내 대답이 정확하고 제안 된 솔루션은 언어와 프레임 워크의 정의 된 특징에만 의존하기 때문에 덜 취약하다고 주장합니다.

Swift 솔루션이 필요한 사람들을 위해 다음은 Swift 에서이 작업을 수행 할 수있는 연장이 있습니다.

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를 대상으로하는 GCC에서 va_list 단순히 C 배열이므로 idS, NSARRAY에게 다음을 채우라고 말하십시오.

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

결과 : 일부 텍스트

웹에서 이것이 가능하다고 주장하는 몇 가지 코드를 찾았지만 직접 수행 할 수 없었지만 인수 수를 미리 알지 못하면 형식 문자열을 동적으로 빌드해야합니다. '요점을 볼 수 있습니다.

배열을 반복하여 문자열을 만드는 것이 좋습니다.

StringByAppendTring : 또는 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

NSARRAY는 런타임에 생성되므로 컴파일 타임 경고를 제공 할 수는 없지만 NSASSERT를 사용하여 지정자 수가 배열 내의 객체 수와 동일한 지 알려줄 수 있습니다.

생성 a 프로젝트 이 카테고리를 찾을 수있는 Github에서. 또한 'StringBeyplacingCharacterSinRange'와 일부 테스트를 사용하여 Chuck의 버전을 추가했습니다.

백만 개 객체를 배열에 사용하고 'stringByReplacingCharacterSinRange가있는 버전 :'스케일이 잘되지 않습니다 (약 2 분 동안 기다렸다가 앱을 닫았습니다). NSMutableString이있는 버전을 사용하여 함수는 약 4 초 안에 문자열을 만들었습니다. 테스트는 시뮬레이터를 사용하여 이루어졌습니다. 사용하기 전에 실제 장치에서 테스트를 수행해야합니다 (사양이 가장 낮은 장치 사용).

편집 : iPhone 5s에서 NSMutableString이있는 버전은 10.471655 (백만 개 객체)를 사용합니다. iPhone 5에서는 21.304876을 사용합니다.

배열을 명시 적으로 만들지 않고 답은 다음과 같습니다.

   NSString *formattedString = [NSString stringWithFormat:@"%@ World, Nice %@", @"Hello", @"Day"];

첫 번째 문자열은 서식 할 대상 문자열이고 다음 문자열은 대상에 삽입 될 문자열입니다.

아니, 당신은 할 수 없습니다. 가변 인수 호출은 컴파일 시간에 해결되며 NSARRAY에는 런타임에만 내용이 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top