Iphone Objective-C/Cocoa-Touch中的HTML字符解码
首先,我发现了这个:,但它对我不起作用 我的编码字符(来自RSS提要,顺便说一句)如下所示:Iphone Objective-C/Cocoa-Touch中的HTML字符解码,iphone,html,objective-c,cocoa,cocoa-touch,Iphone,Html,Objective C,Cocoa,Cocoa Touch,首先,我发现了这个:,但它对我不起作用 我的编码字符(来自RSS提要,顺便说一句)如下所示:& 我搜索了整个网络,找到了相关的讨论,但我的特定编码没有修复,我认为它们被称为十六进制字符。这些被称为。当它们以和#的形式出现时它们称为数字实体引用。基本上,它是应该被替换的字节的字符串表示。在和#038的情况下,它表示ISO-8859-1字符编码方案中值为38的字符,即& 符号必须在RSS中编码的原因是它是一个保留的特殊字符 您需要做的是解析字符串并用与&和之间的值匹配的字节替换实体。在obj
&代码>
我搜索了整个网络,找到了相关的讨论,但我的特定编码没有修复,我认为它们被称为十六进制字符。这些被称为。当它们以和#的形式出现时代码>它们称为数字实体引用。基本上,它是应该被替换的字节的字符串表示。在和#038的情况下
,它表示ISO-8859-1字符编码方案中值为38的字符,即&
符号必须在RSS中编码的原因是它是一个保留的特殊字符
您需要做的是解析字符串并用与&
和之间的值匹配的字节替换实体代码>。在objective C中,我不知道有什么好方法可以做到这一点,但可能会有所帮助
编辑:自从两年前回答这个问题以来,有一些很好的解决方案;请参阅下面的@Michael瀑布的答案。这些被称为。当它们以和#的形式出现时代码>它们称为数字实体引用。基本上,它是应该被替换的字节的字符串表示。在和#038的情况下
,它表示ISO-8859-1字符编码方案中值为38的字符,即&
符号必须在RSS中编码的原因是它是一个保留的特殊字符
您需要做的是解析字符串并用与&
和之间的值匹配的字节替换实体代码>。在objective C中,我不知道有什么好方法可以做到这一点,但可能会有所帮助
编辑:自从两年前回答这个问题以来,有一些很好的解决方案;请参阅下面的@Michael瀑布的答案。我应该将此发布在GitHub或其他网站上。这属于NSString的一个类别,使用NSScanner
实现,处理十六进制和十进制数字字符实体以及通常的符号实体
此外,它还相对优雅地处理格式错误的字符串(当您有一个&后跟一个无效的字符序列时),这在使用此代码的my中是至关重要的
- (NSString *)stringByDecodingXMLEntities {
NSUInteger myLength = [self length];
NSUInteger ampIndex = [self rangeOfString:@"&" options:NSLiteralSearch].location;
// Short-circuit if there are no ampersands.
if (ampIndex == NSNotFound) {
return self;
}
// Make result string with some extra capacity.
NSMutableString *result = [NSMutableString stringWithCapacity:(myLength * 1.25)];
// First iteration doesn't need to scan to & since we did that already, but for code simplicity's sake we'll do it again with the scanner.
NSScanner *scanner = [NSScanner scannerWithString:self];
do {
// Scan up to the next entity or the end of the string.
NSString *nonEntityString;
if ([scanner scanUpToString:@"&" intoString:&nonEntityString]) {
[result appendString:nonEntityString];
}
if ([scanner isAtEnd]) {
goto finish;
}
// Scan either a HTML or numeric character entity reference.
if ([scanner scanString:@"&" intoString:NULL])
[result appendString:@"&"];
else if ([scanner scanString:@"'" intoString:NULL])
[result appendString:@"'"];
else if ([scanner scanString:@""" intoString:NULL])
[result appendString:@"\""];
else if ([scanner scanString:@"<" intoString:NULL])
[result appendString:@"<"];
else if ([scanner scanString:@">" intoString:NULL])
[result appendString:@">"];
else if ([scanner scanString:@"&#" intoString:NULL]) {
BOOL gotNumber;
unsigned charCode;
NSString *xForHex = @"";
// Is it hex or decimal?
if ([scanner scanString:@"x" intoString:&xForHex]) {
gotNumber = [scanner scanHexInt:&charCode];
}
else {
gotNumber = [scanner scanInt:(int*)&charCode];
}
if (gotNumber) {
[result appendFormat:@"%C", charCode];
}
else {
NSString *unknownEntity = @"";
[scanner scanUpToString:@";" intoString:&unknownEntity];
[result appendFormat:@"&#%@%@;", xForHex, unknownEntity];
NSLog(@"Expected numeric character entity but got &#%@%@;", xForHex, unknownEntity);
}
[scanner scanString:@";" intoString:NULL];
}
else {
NSString *unknownEntity = @"";
[scanner scanUpToString:@";" intoString:&unknownEntity];
NSString *semicolon = @"";
[scanner scanString:@";" intoString:&semicolon];
[result appendFormat:@"%@%@", unknownEntity, semicolon];
NSLog(@"Unsupported XML character entity %@%@", unknownEntity, semicolon);
}
}
while (![scanner isAtEnd]);
finish:
return result;
}
-(NSString*)stringByDecodingXMLEntities{
NSU整数myLength=[自身长度];
NSU Integer-ampIndex=[self-rangeOfString:@“&”选项:NSLiteralSearch]。位置;
//如果没有符号,则短路。
if(ampIndex==NSNotFound){
回归自我;
}
//使结果字符串具有一些额外的容量。
NSMutableString*result=[NSMutableString stringWithCapacity:(myLength*1.25)];
//第一次迭代不需要扫描到&因为我们已经这样做了,但为了代码简单起见,我们将使用扫描仪再次进行扫描。
NSScanner*scanner=[NSScanner scannerWithString:self];
做{
//向上扫描到下一个实体或字符串的末尾。
NSString*非实体字符串;
if([scanner scanuptString:@“&”intoString:&nonEntityString]){
[结果追加字符串:非实体字符串];
}
如果([scanner isattend]){
去完成;
}
//扫描HTML或数字字符实体引用。
if([scanner scanString:@“&;”intoString:NULL])
[结果追加字符串:@“&”];
else if([scanner scanString:@“&apos;”intoString:NULL])
[结果追加字符串:@“'”];
else if([scanner scanString:@”“intoString:NULL])
[结果追加字符串:@“\”;
else if([scanner scanString:@”“intoString:NULL])
[结果追加字符串:@”“;
else if([scanner scanString:@“”intoString:NULL]){
布尔数;
无符号字符码;
NSString*xForHex=@;
//是十六进制还是十进制?
if([扫描仪扫描字符串:@“x”intoString:&xForHex]){
gotNumber=[scanner scanHexInt:&charCode];
}
否则{
gotNumber=[scanner scannit:(int*)&charCode];
}
if(gotNumber){
[结果格式:@“%C”,字符码];
}
否则{
NSString*未知性=@“”;
[scanner scanuptString:@];“intoString:&未知性];
[结果附录格式:@“%@%@;”,xForHex,未知性];
NSLog(@“预期的数字字符实体,但获得了%@%@;”,xForHex,未知性);
}
[扫描仪扫描字符串:@”;“intoString:NULL];
}
否则{
NSString*未知性=@“”;
[scanner scanuptString:@];“intoString:&未知性];
NSString*分号=@;
[扫描仪扫描字符串:@”;“intoString:&分号];
[结果附录格式:@“%@%@”,未知度,分号];
NSLog(@“不支持的XML字符实体%@%@”,未知性,分号);
}
}
而(![scanner isattend]);
完成:
返回结果;
}
我应该在GitHub或其他网站上发布这篇文章。这属于NSString的一个类别,使用NSScanner
实现,处理十六进制和十进制数字字符实体以及通常的符号实体
此外,它还相对优雅地处理格式错误的字符串(当您有一个&后跟一个无效的字符序列时),这在使用此代码的my中是至关重要的
- (NSString *)stringByDecodingXMLEntities {
NSUInteger myLength = [self length];
NSUInteger ampIndex = [self rangeOfString:@"&" options:NSLiteralSearch].location;
// Short-circuit if there are no ampersands.
if (ampIndex == NSNotFound) {
return self;
}
// Make result string with some extra capacity.
NSMutableString *result = [NSMutableString stringWithCapacity:(myLength * 1.25)];
// First iteration doesn't need to scan to & since we did that already, but for code simplicity's sake we'll do it again with the scanner.
NSScanner *scanner = [NSScanner scannerWithString:self];
do {
// Scan up to the next entity or the end of the string.
NSString *nonEntityString;
if ([scanner scanUpToString:@"&" intoString:&nonEntityString]) {
[result appendString:nonEntityString];
}
if ([scanner isAtEnd]) {
goto finish;
}
// Scan either a HTML or numeric character entity reference.
if ([scanner scanString:@"&" intoString:NULL])
[result appendString:@"&"];
else if ([scanner scanString:@"'" intoString:NULL])
[result appendString:@"'"];
else if ([scanner scanString:@""" intoString:NULL])
[result appendString:@"\""];
else if ([scanner scanString:@"<" intoString:NULL])
[result appendString:@"<"];
else if ([scanner scanString:@">" intoString:NULL])
[result appendString:@">"];
else if ([scanner scanString:@"&#" intoString:NULL]) {
BOOL gotNumber;
unsigned charCode;
NSString *xForHex = @"";
// Is it hex or decimal?
if ([scanner scanString:@"x" intoString:&xForHex]) {
gotNumber = [scanner scanHexInt:&charCode];
}
else {
gotNumber = [scanner scanInt:(int*)&charCode];
}
if (gotNumber) {
[result appendFormat:@"%C", charCode];
}
else {
NSString *unknownEntity = @"";
[scanner scanUpToString:@";" intoString:&unknownEntity];
[result appendFormat:@"&#%@%@;", xForHex, unknownEntity];
NSLog(@"Expected numeric character entity but got &#%@%@;", xForHex, unknownEntity);
}
[scanner scanString:@";" intoString:NULL];
}
else {
NSString *unknownEntity = @"";
[scanner scanUpToString:@";" intoString:&unknownEntity];
NSString *semicolon = @"";
[scanner scanString:@";" intoString:&semicolon];
[result appendFormat:@"%@%@", unknownEntity, semicolon];
NSLog(@"Unsupported XML character entity %@%@", unknownEntity, semicolon);
}
}
while (![scanner isAtEnd]);
finish:
return result;
}
-(NSString*)stringByDecodingXMLEntities{
NSU整数myLength=[自身长度];
NSU Integer-ampIndex=[self-rangeOfString:@“&”选项:NSLiteralSearch]。位置;
//如果没有符号,则短路。
if(ampIndex==NSNotFound){
回归自我;
}
//使结果字符串具有一些额外的容量。
NSMutableString*结果=[NSM
- (NSString *)stringByDecodingXMLEntities {
NSUInteger myLength = [self length];
NSUInteger ampIndex = [self rangeOfString:@"&" options:NSLiteralSearch].location;
// Short-circuit if there are no ampersands.
if (ampIndex == NSNotFound) {
return self;
}
// Make result string with some extra capacity.
NSMutableString *result = [NSMutableString stringWithCapacity:(myLength * 1.25)];
// First iteration doesn't need to scan to & since we did that already, but for code simplicity's sake we'll do it again with the scanner.
NSScanner *scanner = [NSScanner scannerWithString:self];
[scanner setCharactersToBeSkipped:nil];
NSCharacterSet *boundaryCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r;"];
do {
// Scan up to the next entity or the end of the string.
NSString *nonEntityString;
if ([scanner scanUpToString:@"&" intoString:&nonEntityString]) {
[result appendString:nonEntityString];
}
if ([scanner isAtEnd]) {
goto finish;
}
// Scan either a HTML or numeric character entity reference.
if ([scanner scanString:@"&" intoString:NULL])
[result appendString:@"&"];
else if ([scanner scanString:@"'" intoString:NULL])
[result appendString:@"'"];
else if ([scanner scanString:@""" intoString:NULL])
[result appendString:@"\""];
else if ([scanner scanString:@"<" intoString:NULL])
[result appendString:@"<"];
else if ([scanner scanString:@">" intoString:NULL])
[result appendString:@">"];
else if ([scanner scanString:@"&#" intoString:NULL]) {
BOOL gotNumber;
unsigned charCode;
NSString *xForHex = @"";
// Is it hex or decimal?
if ([scanner scanString:@"x" intoString:&xForHex]) {
gotNumber = [scanner scanHexInt:&charCode];
}
else {
gotNumber = [scanner scanInt:(int*)&charCode];
}
if (gotNumber) {
[result appendFormat:@"%C", (unichar)charCode];
[scanner scanString:@";" intoString:NULL];
}
else {
NSString *unknownEntity = @"";
[scanner scanUpToCharactersFromSet:boundaryCharacterSet intoString:&unknownEntity];
[result appendFormat:@"&#%@%@", xForHex, unknownEntity];
//[scanner scanUpToString:@";" intoString:&unknownEntity];
//[result appendFormat:@"&#%@%@;", xForHex, unknownEntity];
NSLog(@"Expected numeric character entity but got &#%@%@;", xForHex, unknownEntity);
}
}
else {
NSString *amp;
[scanner scanString:@"&" intoString:&]; //an isolated & symbol
[result appendString:amp];
/*
NSString *unknownEntity = @"";
[scanner scanUpToString:@";" intoString:&unknownEntity];
NSString *semicolon = @"";
[scanner scanString:@";" intoString:&semicolon];
[result appendFormat:@"%@%@", unknownEntity, semicolon];
NSLog(@"Unsupported XML character entity %@%@", unknownEntity, semicolon);
*/
}
}
while (![scanner isAtEnd]);
finish:
return result;
}
-(NSString*) decodeHtmlUnicodeCharacters: (NSString*) html {
NSString* result = [html copy];
NSArray* matches = [result arrayOfCaptureComponentsMatchedByRegex: @"\\&#([\\d]+);"];
if (![matches count])
return result;
for (int i=0; i<[matches count]; i++) {
NSArray* array = [matches objectAtIndex: i];
NSString* charCode = [array objectAtIndex: 1];
int code = [charCode intValue];
NSString* character = [NSString stringWithFormat:@"%C", code];
result = [result stringByReplacingOccurrencesOfString: [array objectAtIndex: 0]
withString: character];
}
return result;
+ (NSString*) decodeHtmlUnicodeCharactersToString:(NSString*)str
{
NSMutableString* string = [[NSMutableString alloc] initWithString:str]; // #&39; replace with '
NSString* unicodeStr = nil;
NSString* replaceStr = nil;
int counter = -1;
for(int i = 0; i < [string length]; ++i)
{
unichar char1 = [string characterAtIndex:i];
for (int k = i + 1; k < [string length] - 1; ++k)
{
unichar char2 = [string characterAtIndex:k];
if (char1 == '&' && char2 == '#' )
{
++counter;
unicodeStr = [string substringWithRange:NSMakeRange(i + 2 , 2)];
// read integer value i.e, 39
replaceStr = [string substringWithRange:NSMakeRange (i, 5)]; // #&39;
[string replaceCharactersInRange: [string rangeOfString:replaceStr] withString:[NSString stringWithFormat:@"%c",[unicodeStr intValue]]];
break;
}
}
}
[string autorelease];
if (counter > 1)
return [self decodeHtmlUnicodeCharactersToString:string];
else
return string;
}
- (NSString *)stringByConvertingHTMLToPlainText;
- (NSString *)stringByDecodingHTMLEntities;
- (NSString *)stringByEncodingHTMLEntities;
- (NSString *)stringWithNewLinesAsBRs;
- (NSString *)stringByRemovingNewLinesAndWhitespace;
/// Get a string where internal characters that are escaped for HTML are unescaped
//
/// For example, '&' becomes '&'
/// Handles   and 2 cases as well
///
// Returns:
// Autoreleased NSString
//
- (NSString *)gtm_stringByUnescapingFromHTML;
@interface NSString (NSStringCategory)
- (NSString *) stringByReplacingISO8859Codes;
@end
@implementation NSString (NSStringCategory)
- (NSString *) stringByReplacingISO8859Codes
{
NSString *dataString = self;
do {
//*** See if string contains &# prefix
NSRange range = [dataString rangeOfString: @"&#" options: NSRegularExpressionSearch];
if (range.location == NSNotFound) {
break;
}
//*** Get the next three charaters after the prefix
NSString *isoHex = [dataString substringWithRange: NSMakeRange(range.location + 2, 3)];
//*** Create the full code for replacement
NSString *isoString = [NSString stringWithFormat: @"&#%@;", isoHex];
//*** Convert to decimal integer
unsigned decimal = 0;
NSScanner *scanner = [NSScanner scannerWithString: [NSString stringWithFormat: @"0%@", isoHex]];
[scanner scanHexInt: &decimal];
//*** Use decimal code to get unicode character
NSString *unicode = [NSString stringWithFormat:@"%C", decimal];
//*** Replace all occurences of this code in the string
dataString = [dataString stringByReplacingOccurrencesOfString: isoString withString: unicode];
} while (TRUE); //*** Loop until we hit the NSNotFound
return dataString;
}
@end
NSString *htmlString = @" & & < > ™ © ♥ ♣ ♠ ♦";
NSData *stringData = [htmlString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *options = @{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType};
NSAttributedString *decodedString;
decodedString = [[NSAttributedString alloc] initWithData:stringData
options:options
documentAttributes:NULL
error:NULL];
extension String {
static private let mappings = [""" : "\"","&" : "&", "<" : "<", ">" : ">"," " : " ","¡" : "¡","¢" : "¢","£" : " £","¤" : "¤","¥" : "¥","¦" : "¦","§" : "§","¨" : "¨","©" : "©","ª" : " ª","«" : "«","¬" : "¬","®" : "®","¯" : "¯","°" : "°","±" : "±","² " : "²","³" : "³","´" : "´","µ" : "µ","¶" : "¶","·" : "·","¸" : "¸","¹" : "¹","º" : "º","»" : "»&","frac14" : "¼","½" : "½","¾" : "¾","¿" : "¿","×" : "×","÷" : "÷","Ð" : "Ð","ð" : "ð","Þ" : "Þ","þ" : "þ","Æ" : "Æ","æ" : "æ","&OElig" : "Œ","&oelig" : "œ","Å" : "Å","Ø" : "Ø","Ç" : "Ç","ç" : "ç","ß" : "ß","Ñ" : "Ñ","ñ":"ñ",]
func stringByDecodingXMLEntities() -> String {
guard let _ = self.rangeOfString("&", options: [.LiteralSearch]) else {
return self
}
var result = ""
let scanner = NSScanner(string: self)
scanner.charactersToBeSkipped = nil
let boundaryCharacterSet = NSCharacterSet(charactersInString: " \t\n\r;")
repeat {
var nonEntityString: NSString? = nil
if scanner.scanUpToString("&", intoString: &nonEntityString) {
if let s = nonEntityString as? String {
result.appendContentsOf(s)
}
}
if scanner.atEnd {
break
}
var didBreak = false
for (k,v) in String.mappings {
if scanner.scanString(k, intoString: nil) {
result.appendContentsOf(v)
didBreak = true
break
}
}
if !didBreak {
if scanner.scanString("&#", intoString: nil) {
var gotNumber = false
var charCodeUInt: UInt32 = 0
var charCodeInt: Int32 = -1
var xForHex: NSString? = nil
if scanner.scanString("x", intoString: &xForHex) {
gotNumber = scanner.scanHexInt(&charCodeUInt)
}
else {
gotNumber = scanner.scanInt(&charCodeInt)
}
if gotNumber {
let newChar = String(format: "%C", (charCodeInt > -1) ? charCodeInt : charCodeUInt)
result.appendContentsOf(newChar)
scanner.scanString(";", intoString: nil)
}
else {
var unknownEntity: NSString? = nil
scanner.scanUpToCharactersFromSet(boundaryCharacterSet, intoString: &unknownEntity)
let h = xForHex ?? ""
let u = unknownEntity ?? ""
result.appendContentsOf("&#\(h)\(u)")
}
}
else {
scanner.scanString("&", intoString: nil)
result.appendContentsOf("&")
}
}
} while (!scanner.atEnd)
return result
}
}
NSString *unicodePoint = @"2318"
unichar iconChar = (unichar) strtoul(unicodePoint.UTF8String, NULL, 16);
NSString *recoded = [NSString stringWithFormat:@"%C", iconChar];
NSLog(@"recoded: %@", recoded");
// prints out "recoded: ⌘"
extension String {
static private let mappings = [""" : "\"","&" : "&", "<" : "<", ">" : ">"," " : " ","¡" : "¡","¢" : "¢","£" : " £","¤" : "¤","¥" : "¥","¦" : "¦","§" : "§","¨" : "¨","©" : "©","ª" : " ª","«" : "«","¬" : "¬","®" : "®","¯" : "¯","°" : "°","±" : "±","² " : "²","³" : "³","´" : "´","µ" : "µ","¶" : "¶","·" : "·","¸" : "¸","¹" : "¹","º" : "º","»" : "»&","frac14" : "¼","½" : "½","¾" : "¾","¿" : "¿","×" : "×","÷" : "÷","Ð" : "Ð","ð" : "ð","Þ" : "Þ","þ" : "þ","Æ" : "Æ","æ" : "æ","&OElig" : "Œ","&oelig" : "œ","Å" : "Å","Ø" : "Ø","Ç" : "Ç","ç" : "ç","ß" : "ß","Ñ" : "Ñ","ñ":"ñ",]
func stringByDecodingXMLEntities() -> String {
guard let _ = self.range(of: "&", options: [.literal]) else {
return self
}
var result = ""
let scanner = Scanner(string: self)
scanner.charactersToBeSkipped = nil
let boundaryCharacterSet = CharacterSet(charactersIn: " \t\n\r;")
repeat {
var nonEntityString: NSString? = nil
if scanner.scanUpTo("&", into: &nonEntityString) {
if let s = nonEntityString as? String {
result.append(s)
}
}
if scanner.isAtEnd {
break
}
var didBreak = false
for (k,v) in String.mappings {
if scanner.scanString(k, into: nil) {
result.append(v)
didBreak = true
break
}
}
if !didBreak {
if scanner.scanString("&#", into: nil) {
var gotNumber = false
var charCodeUInt: UInt32 = 0
var charCodeInt: Int32 = -1
var xForHex: NSString? = nil
if scanner.scanString("x", into: &xForHex) {
gotNumber = scanner.scanHexInt32(&charCodeUInt)
}
else {
gotNumber = scanner.scanInt32(&charCodeInt)
}
if gotNumber {
let newChar = String(format: "%C", (charCodeInt > -1) ? charCodeInt : charCodeUInt)
result.append(newChar)
scanner.scanString(";", into: nil)
}
else {
var unknownEntity: NSString? = nil
scanner.scanUpToCharacters(from: boundaryCharacterSet, into: &unknownEntity)
let h = xForHex ?? ""
let u = unknownEntity ?? ""
result.append("&#\(h)\(u)")
}
}
else {
scanner.scanString("&", into: nil)
result.append("&")
}
}
} while (!scanner.isAtEnd)
return result
}
}