我从XML源获取数据并使用tbxml解析它。一切都工作正常,直到我得到像“é”这样的拉丁字母,它将显示为: 代码:
é
我没有看到NSString进行转换的正确方法。有什么想法吗?
答案 0 :(得分:4)
您可以使用正则表达式。正则表达式是所有问题的解决方案和原因! :)
下面的示例至少在撰写本文时使用了未发布的RegexKitLite 4.0。您可以通过svn获取4.0开发快照:
shell% svn co http://regexkit.svn.sourceforge.net/svnroot/regexkit regexkit
以下示例利用新的4.0 Blocks功能来搜索和替换é
个字符实体。
第一个例子是两者中的“更简单”。它仅处理像é
这样的十进制字符实体,而不处理像é
这样的十六进制字符实体。如果你可以保证你永远不会有十六进制字符实体,那么这应该没问题:
#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.
替换块中间的“三行”确保正确转换UTF-32
字符&gt; 0xFFFF
。为了完整性和正确性,我把它放进去了。无效的UTF-32
字符值(0xd800
- 0xdfff
)会被转为U+FFFD
或REPLACEMENT CHARACTER
。如果你能“保证”你永远不会有&#...;
个字符实体,那么&gt; 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) {
您问题的另一个答案指向this Stack Overflow Question with an Answer。此解决方案与该解决方案之间的差异(仅基于快速一次):
此解决方案:
另一种解决方案:
UTF-32
字符代码点时可能不太正确(在实践中可能不是问题)。>
。不过,这可以很容易地添加到上面。我没有对这两种解决方案进行基准测试,但是我愿意投入大笔资金,使用RKLRegexEnumerationFastCapturedStringsXXX
的RegexKitLite解决方案胜过NSScanner
解决方案。
如果您真的想添加命名字符实体,可以将正则表达式更改为:
NSString *regex = @"&(?:#(?:([0-9]+)|x([0-9a-fA-F]+))|([a-zA-Z][a-zA-Z0-9]+));";
注意:我根本没有测试过上述内容。
Capture#3应包含“字符实体名称”,然后您可以使用它来查找。一个非常奇特的方法是让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;
匹配文本(即“什么都不做”)。
答案 1 :(得分:3)
这似乎是一个非常常见的问题。查看HTML character decoding in Objective-C / Cocoa Touch