我正在从XML来源获取数据并使用tbxml解析。一切正常,直到我遇到像“é”这样的拉丁字母时,它将显示为:
Code:
我看不到NSString的合适方法来进行转换。有什么想法吗?
é
我看不到NSString的合适方法来进行转换。有什么想法吗?
é
字符实体。é
而不处理十六进制字符实体如é
。如果您可以保证永远不会使用十六进制字符实体,这将很好:#import <Foundation/Foundation.h>
#import "RegexKitLite.h"
int main(int argc, char *charv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *string = @"A test: é and é ? YAY! Even >0xffff are handled: 𝐀 or 𝐀, see? (0x1d400 == MATHEMATICAL BOLD CAPITAL A)";
NSString *regex = @"&#([0-9]+);";
NSString *replacedString = [string stringByReplacingOccurrencesOfRegex:regex usingBlock:^NSString *(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) {
NSUInteger u16Length = 0UL, u32_ch = [capturedStrings[1] integerValue];
UniChar u16Buffer[3];
if (u32_ch <= 0xFFFFU) { u16Buffer[u16Length++] = ((u32_ch >= 0xD800U) && (u32_ch <= 0xDFFFU)) ? 0xFFFDU : u32_ch; }
else if (u32_ch > 0x10FFFFU) { u16Buffer[u16Length++] = 0xFFFDU; }
else { u32_ch -= 0x0010000UL; u16Buffer[u16Length++] = ((u32_ch >> 10) + 0xD800U); u16Buffer[u16Length++] = ((u32_ch & 0x3FFUL) + 0xDC00U); }
return([NSString stringWithCharacters:u16Buffer length:u16Length]);
}];
NSLog(@"replaced: '%@'", replacedString);
return(0);
}
使用以下命令进行编译和运行:
shell% gcc -arch i386 -g -o charReplace charReplace.m RegexKitLite.m -framework Foundation -licucore
shell% ./charReplace
2010-02-13 22:51:48.909 charReplace[35527:903] replaced: 'A test: é and é ? YAY! Even >0xffff are handled: or 𝐀, see? (0x1d400 == MATHEMATICAL BOLD CAPITAL A)'
0x1d4000这个字符可能在您的浏览器中无法显示,但在终端窗口中它看起来像一个粗体的A。
替换块中间的“三条线”确保正确转换大于0xFFFF
的UTF-32
字符。我加上这个是为了完整性和正确性。无效的UTF-32
字符值(0xd800
-0xdfff
)会被转换成U+FFFD
或REPLACEMENT CHARACTER
。如果您可以"保证"您永远不会有大于0xFFFF
(或65535
)的&#...;
字符实体,并且始终是“合法的”UTF-32
,那么您可以删除这些行并将整个块简化为以下内容:
return([NSString stringWithFormat:@"%C", [capturedStrings[1] integerValue]]);
#import <Foundation/Foundation.h>
#import "RegexKitLite.h"
int main(int argc, char *charv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *string = @"A test: é and é ? YAY! Even >0xffff are handled: 𝐀 or 𝐀, see? (0x1d400 == MATHEMATICAL BOLD CAPITAL A)";
NSString *regex = @"&#(?:([0-9]+)|x([0-9a-fA-F]+));";
NSString *replacedString = [string stringByReplacingOccurrencesOfRegex:regex usingBlock:^NSString *(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) {
NSUInteger u16Length = 0UL, u32_ch = 0UL;
UniChar u16Buffer[3];
CFStringRef cfSelf = (capturedRanges[1].location != NSNotFound) ? (CFStringRef)capturedStrings[1] : (CFStringRef)capturedStrings[2];
UInt8 buffer[64];
const char *cptr;
if((cptr = CFStringGetCStringPtr(cfSelf, kCFStringEncodingMacRoman)) == NULL) {
CFRange range = CFRangeMake(0L, CFStringGetLength(cfSelf));
CFIndex usedBytes = 0L;
CFStringGetBytes(cfSelf, range, kCFStringEncodingUTF8, '?', false, buffer, 60L, &usedBytes);
buffer[usedBytes] = 0;
cptr = (const char *)buffer;
}
u32_ch = strtoul(cptr, NULL, (capturedRanges[1].location != NSNotFound) ? 10 : 16);
if (u32_ch <= 0xFFFFU) { u16Buffer[u16Length++] = ((u32_ch >= 0xD800U) && (u32_ch <= 0xDFFFU)) ? 0xFFFDU : u32_ch; }
else if (u32_ch > 0x10FFFFU) { u16Buffer[u16Length++] = 0xFFFDU; }
else { u32_ch -= 0x0010000UL; u16Buffer[u16Length++] = ((u32_ch >> 10) + 0xD800U); u16Buffer[u16Length++] = ((u32_ch & 0x3FFUL) + 0xDC00U); }
return([NSString stringWithCharacters:u16Buffer length:u16Length]);
}];
NSLog(@"replaced: '%@'", replacedString);
return(0);
}
再次编译并运行:
shell% gcc -arch i386 -g -o charReplace charReplace.m RegexKitLite.m -framework Foundation -licucore
shell% ./charReplace
2010-02-13 22:52:02.182 charReplace[35540:903] replaced: 'A test: é and é ? YAY! Even >0xffff are handled: or , see? (0x1d400 == MATHEMATICAL BOLD CAPITAL A)'
é
,而在此输出中已替换。再次强调,略微有些冗长,但是我选择追求完整性和正确性。stringByReplacingOccurrencesOfRegex:
方法进行替换,但您应该参考文档以了解使用RKLRegexEnumerationFastCapturedStringsXXX
的注意事项。重要的是要注意,在上述用法中使用它不是问题,并且非常安全(这也是我为RegexKitLite添加选项的原因之一)。 NSString *replacedString = [string stringByReplacingOccurrencesOfRegex:regex options:RKLNoOptions inRange:NSMakeRange(0UL, [string length]) error:NULL enumerationOptions:RKLRegexEnumerationFastCapturedStringsXXX usingBlock:^NSString *(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) {
另一个回答您的问题指向了这个Stack Overflow帖子和答案。这种解决方案与那种解决方案的不同(只是快速浏览):
这个解决方案:
另一个解决方案:
>
。这很容易添加到上面。我还没有对任何解决方案进行基准测试,但我愿意打赌大量资金,使用RKLRegexEnumerationFastCapturedStringsXXX
的RegexKitLite解决方案比NSScanner
解决方案更胜一筹。
如果您真的想添加命名字符实体,您可以将正则表达式更改为以下内容:
NSString *regex = @"&(?:#(?:([0-9]+)|x([0-9a-fA-F]+))|([a-zA-Z][a-zA-Z0-9]+));";
注意: 我完全没有测试上述内容。NSDictionary
,其中包含命名字符作为key
和包含该名称映射到的字符的NSString
object
。您甚至可以将整个内容保存为外部.plist
资源,并使用类似以下内容的东西惰性地按需加载:NSDictionary *namedCharactersDictionary = [NSDictionary dictionaryWithContentsOfFile:@"namedCharacters.plist"];
显然,您需要对其进行微调,以使用NSBundle
获取应用程序资源目录的路径,但您可以得到这个想法。 然后,在Block中添加另一个条件检查:
if(capturedRanges[3].location != NSNotFound) {
NSString *namedCharacter = [namedCharactersDictionary objectForKey:capturedStrings[3]];
return((namedCharacter == NULL) ? capturedStrings[0] : namedCharacter);
}
¬found;
匹配文本(即“不做任何事情”)。