如何通过将每个 unichar 从中拉出来枚举 NSString?我可以使用 characterAtIndex 但这比通过递增的 unichar* 来做要慢。我在 Apple 的文档中没有看到不需要将字符串复制到第二个缓冲区的任何内容。
像这样的东西是理想的:
for (unichar c in string) { ... }
或者
unichar* ptr = (unichar*)string;
如何通过将每个 unichar 从中拉出来枚举 NSString?我可以使用 characterAtIndex 但这比通过递增的 unichar* 来做要慢。我在 Apple 的文档中没有看到不需要将字符串复制到第二个缓冲区的任何内容。
像这样的东西是理想的:
for (unichar c in string) { ... }
或者
unichar* ptr = (unichar*)string;
-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);
}
编辑:看来CFString
参考包含以下方法:
const UniChar *CFStringGetCharactersPtr(CFStringRef theString);
这意味着您可以执行以下操作:
const unichar *chars = CFStringGetCharactersPtr((__bridge CFStringRef) theString);
while (*chars)
{
// do something with *chars
chars++;
}
如果您不想为处理缓冲区分配内存,那么这就是要走的路。
您唯一的选择是将字符复制到新缓冲区中。这是因为 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];
}
如果您使用可能很长的字符串,最好分配一个固定大小的缓冲区并以块的形式枚举字符串(这实际上是枚举的工作速度)。
getCharacters:range:
根据ughoavgfhw在他的回答中的建议,我创建了一个块式枚举方法,该方法与固定大小的缓冲区一起使用。它避免了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;
}
}
}
}
我所知道的枚举字符的最快可靠方法NSString
是使用这个隐藏在视线中的相对鲜为人知的核心基础宝石(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()
返回NULL
、CFStringGetCStringPtr()
返回NULL
、默认为较慢CFStringGetCharacters()
以及将字符缓存在 C 数组中以尽可能快地访问。这个 API 确实值得更多的宣传。
需要注意的是,如果您CFStringInlineBuffer
在非零偏移量处初始化 ,则应将相对字符索引传递给CFStringInlineBuffer()
,如标题注释中所述:
假设您正在进行顺序或本地化访问,接下来的两个函数允许快速访问字符串的内容。要使用,请
CFStringInitInlineBuffer()
使用CFStringInlineBuffer
(例如在堆栈上)和要查看的字符串中的范围进行调用。然后根据需要调用任意CFStringGetCharacterFromInlineBuffer()
多次,并使用该范围内的索引(相对于该范围的开头)。这些是 INLINE 函数,最终CFString
只会偶尔调用一次,以填充缓冲区。CFStringGetCharacterFromInlineBuffer()
如果指定了原始范围之外的位置,则返回 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
我不认为你能做到这一点。NSString
是许多类的抽象接口,这些类不保证字符数据的内部存储,因此完全有可能没有字符数组来获取指针。
如果您的问题中提到的选项都不适合您的应用程序,我建议您为此创建自己的字符串类,或者使用原始 malloc 的 unichar 数组而不是字符串对象。