通过指针枚举NSString字符

时间:2012-04-17 20:48:03

标签: objective-c ios string cocoa-touch

如何通过拉出每个unichar来枚举NSString?我可以使用characterAtIndex,但这比通过递增的unichar *更慢。我没有在Apple的文档中看到任何不需要将字符串复制到第二个缓冲区的内容。

这样的事情是理想的:

for (unichar c in string) { ... }

unichar* ptr = (unichar*)string;

6 个答案:

答案 0 :(得分:11)

您可以先将-characterAtIndex:转换为IMP表单来加快NSString *str = @"This is a test"; NSUInteger len = [str length]; // only calling [str length] once speeds up the process as well SEL sel = @selector(characterAtIndex:); // using typeof to save my fingers from typing more unichar (*charAtIdx)(id, SEL, NSUInteger) = (typeof(charAtIdx)) [str methodForSelector:sel]; for (int i = 0; i < len; i++) { unichar c = charAtIdx(str, sel, i); // do something with C NSLog(@"%C", c); } 的速度:

const UniChar *CFStringGetCharactersPtr(CFStringRef theString);

编辑:似乎CFString Reference包含以下方法:

const unichar *chars = CFStringGetCharactersPtr((__bridge CFStringRef) theString);

while (*chars)
{
    // do something with *chars
    chars++;
}

这意味着您可以执行以下操作:

{{1}}

如果您不想分配内存来处理缓冲区,那就可以了。

答案 1 :(得分:4)

您唯一的选择是将字符复制到新缓冲区中。这是因为NSString类不保证您可以使用内部缓冲区。最好的方法是使用getCharacters:range:方法。

NSUInteger i, length = [string length];
unichar *buffer = malloc(sizeof(unichar) * length);
NSRange range = {0,length};
[string getCharacters:buffer range:range];
for(i = 0; i < length; ++i) {
    unichar c = buffer[i];
}

如果你正在使用可能很长的字符串,最好分配一个固定大小的缓冲区并以块的形式枚举字符串(这实际上是枚举工作的速度)。

答案 2 :(得分:1)

根据ughoavgfhw在他的回答中提出的建议,我创建了一个使用getCharacters:range:和固定大小缓冲区的块样式枚举方法。它避免了CFStringGetCharactersPtr返回null并且不必malloc大缓冲区的情况。您可以将其放入NSString类别,或者根据需要修改它以将字符串作为参数。

-(void)enumerateCharactersWithBlock:(void (^)(unichar, NSUInteger, BOOL *))block
{
    const NSInteger bufferSize = 16;
    const NSInteger length = [self length];
    unichar buffer[bufferSize];
    NSInteger bufferLoops = (length - 1) / bufferSize + 1;
    BOOL stop = NO;
    for (int i = 0; i < bufferLoops; i++) {
        NSInteger bufferOffset = i * bufferSize;
        NSInteger charsInBuffer = MIN(length - bufferOffset, bufferSize);
        [self getCharacters:buffer range:NSMakeRange(bufferOffset, charsInBuffer)];
        for (int j = 0; j < charsInBuffer; j++) {
            block(buffer[j], j + bufferOffset, &stop);
            if (stop) {
                return;
            }
        }
    }
}

答案 3 :(得分:0)

这将有效:

char *s = [string UTF8String];
for (char *t = s; *t; t++)
  /* use as */ *t;

[编辑]如果你真的需要unicode字符,那么你别无选择,只能使用 length characterAtIndex 。来自文档:

  

NSString类有两个原始方法-length和characterAtIndex: - 为其界面中的所有其他方法提供基础。 length方法返回字符串中Unicode字符的总数。 characterAtIndex:通过索引访问字符串中的每个字符,索引值从0开始。

所以你的代码是:

  for (int index = 0; index < string.length; index++)
    { 
      unichar c = [string characterAtIndex: index];
      /* ... */
    }

[编辑2]

另外,不要忘记NSString是对CFString的“免费桥接”,因此所有非Objective-C直接C代码接口函数都是可用的。相关的是CFStringGetCharacterAtIndex

答案 4 :(得分:0)

我认为你不能这样做。 NSString是许多类的抽象接口,它们不保证字符数据的内部存储,因此完全有可能没有字符数组来获取指针。

如果你问题中提到的两个选项都不适合你的应用,我建议为此目的创建自己的字符串类,或者使用原始的malloc'ed unichar数组而不是字符串对象。

答案 5 :(得分:0)

我所知道的枚举NSString中字符的最快可靠方法是使用这种鲜为人知的,隐藏在普通视线中的Core Foundation宝石(CFString.h)。

NSString *string = <#initialize your string#>
NSUInteger stringLength = string.length;
CFStringInlineBuffer buf;
CFStringInitInlineBuffer((__bridge CFStringRef) string, &buf, (CFRange) { 0, stringLength });

for (NSUInteger charIndex = 0; charIndex < stringLength; charIndex++) {
    unichar c = CFStringGetCharacterFromInlineBuffer(&buf, charIndex);
}

如果您查看这些内联函数CFStringInitInlineBuffer()CFStringGetCharacterFromInlineBuffer()的源代码,您会发现它们处理了所有令人讨厌的细节,例如CFStringGetCharactersPtr()返回NULLCFStringGetCStringPtr()返回NULL,默认为慢速CFStringGetCharacters()并将字符缓存在C数组中,以实现最快的访问速度。该API确实值得更多宣传。

需要注意的是,如果将CFStringInlineBuffer初始化为非零偏移量,则应将相对字符索引传递给CFStringInlineBuffer(),如标题注释中所述:

  

接下来的两个函数允许快速访问字符串的内容,前提是您要进行顺序访问或本地访问。要使用该函数,请调用CFStringInitInlineBuffer()CFStringInlineBuffer(例如在堆栈上),并在字符串中查找一个范围。然后根据需要多次调用CFStringGetCharacterFromInlineBuffer()使用该范围内的索引(相对于该范围的开始)。这些是INLINE函数,最终只会偶尔调用CFString来填充缓冲区。如果指定的位置超出了原始范围,则CFStringGetCharacterFromInlineBuffer()返回0。