Objective-C / Cocoa Touch에서의 HTML 문자 디코딩
먼저, 저는 이것을 찾았습니다: Objective C HTML 이스케이프/에스케이프 해제, 하지만 저에게는 작동하지 않습니다.
암호화된 문자(RSS 피드, btw)는 다음과 같습니다.&
인터넷을 검색해보니 관련 토론이 있었는데, 제 특정 인코딩에 대한 수정이 없어서 16진수 문자라고 하는 것 같습니다.
HTML에 대한 내 NSString 범주를 확인하십시오.사용 가능한 방법은 다음과 같습니다.
- (NSString *)stringByConvertingHTMLToPlainText;
- (NSString *)stringByDecodingHTMLEntities;
- (NSString *)stringByEncodingHTMLEntities;
- (NSString *)stringWithNewLinesAsBRs;
- (NSString *)stringByRemovingNewLinesAndWhitespace;
Daniel의 작품은 기본적으로 매우 훌륭하며, 저는 몇 가지 문제를 해결했습니다.
NSSCanner에 대한 건너뛰기 문자가 제거되었습니다. 그렇지 않으면 연속된 두 엔티티 사이의 공백이 무시됩니다.
[스캐너 세트 건너뛸 문자:nil];
분리된 '&' 기호가 있을 때 구문 분석을 수정했습니다(이것에 대한 '올바른' 출력이 무엇인지 확신할 수 없습니다. 파이어폭스와 비교했을 뿐입니다).
예.
&#ABC DF & B' & C' Items (288)
수정된 코드는 다음과 같습니다.
- (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;
}
iOS 7부터, 당신은 HTML 문자를 사용하여 네이티브로 디코딩할 수 있습니다.NSAttributedString와 함께NSHTMLTextDocumentType속성:
NSString *htmlString = @" & & < > ™ © ♥ ♣ ♠ ♦";
NSData *stringData = [htmlString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *options = @{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType};
NSAttributedString *decodedString;
decodedString = [[NSAttributedString alloc] initWithData:stringData
options:options
documentAttributes:NULL
error:NULL];
디코딩된 속성 문자열은 이제 & < > ™ © ♥ ♣ ♠ ♦로 표시됩니다.
참고: 메인 스레드에서 호출된 경우에만 작동합니다.
이러한 항목을 문자 엔터티 참조라고 합니다.그들이 형태를 취할 때&#<number>;숫자 도면요소 참조라고 합니다.기본적으로, 이것은 대체되어야 하는 바이트의 문자열 표현입니다.의 경우에는&ISO-8859-1 문자 인코딩 체계에서 38의 값을 가진 문자를 나타냅니다.&.
앰퍼샌드가 RSS로 인코딩되어야 하는 이유는 예약된 특수 문자이기 때문입니다.
문자열을 구문 분석하고 엔티티를 다음 사이의 값과 일치하는 바이트로 교체해야 합니다.&#그리고.;객관적인 C에서 이것을 할 수 있는 좋은 방법은 모르겠지만, 이 스택 오버플로 질문이 도움이 될 수 있습니다.
편집: 2년 전에 이것에 답한 이후로 몇 가지 훌륭한 해결책이 있습니다. 아래의 @Michael Waterfall의 대답을 참조하십시오.
아무도 가장 간단한 옵션 중 하나를 언급하지 않는 것 같습니다.Mac용 Google 도구 상자
(이름에도 불구하고, 이것은 iOS에서도 작동합니다.)
https://github.com/google/google-toolbox-for-mac/blob/master/Foundation/GTMNSString%2BHTML.h
/// 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;
프로젝트에 헤더, 구현 및 세 개의 파일만 포함해야 했습니다.GTMDefines.h.
GitHub 같은 곳에 이걸 올려야겠어요., "NSString", "NSString"을 사용합니다.NSScanner구현을 위해 16진수 및 10진수 문자 엔티티와 일반적인 기호 엔티티를 모두 처리합니다.
또한 잘못된 형식의 문자열( & 뒤에 잘못된 문자 시퀀스가 있을 때)을 비교적 우아하게 처리하는데, 이는 이 코드를 사용하는 제가 출시한 앱에서 중요한 것으로 나타났습니다.
- (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;
}
RegexKitLite 프레임워크를 사용하는 방법은 다음과 같습니다.
-(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;
}
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
}
}
실제로 마이클 워터폴의 훌륭한 MWFeed Parser 프레임워크(그의 답변 참조)는 ARC 지원으로 업데이트한 rmchara에 의해 포크되었습니다!
여기 Github에서 찾을 수 있습니다.
정말 잘 작동합니다. stringByDecoding을 사용했습니다.HTML 엔티티 메소드 및 완벽하게 작동합니다.
마치 다른 해결책이 필요한 것처럼!이것은 매우 간단하고 효과적입니다.
@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
를 사용하는 경우, 예@"2318"다음을 사용하여 올바른 유니코드 문자로 기록된 NSString을 추출할 수 있습니다.strtoul;
NSString *unicodePoint = @"2318"
unichar iconChar = (unichar) strtoul(unicodePoint.UTF8String, NULL, 16);
NSString *recoded = [NSString stringWithFormat:@"%C", iconChar];
NSLog(@"recoded: %@", recoded");
// prints out "recoded: ⌘"
주갈레의 대답의 스위프트 3 버전
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
}
}
언급URL : https://stackoverflow.com/questions/1105169/html-character-decoding-in-objective-c-cocoa-touch
'programing' 카테고리의 다른 글
| 파일 대화상자를 열고 WPF 컨트롤 및 C#을 사용하여 파일을 선택합니다. (0) | 2023.06.03 |
|---|---|
| Express.js란 무엇입니까? (0) | 2023.06.03 |
| SQL Server 테이블 간에 행을 복사하는 방법 (0) | 2023.06.03 |
| HTTP를 통해 이진 파일을 다운로드하려면 어떻게 해야 합니까? (0) | 2023.06.03 |
| 모든 Git 푸시에서 사용자 이름과 암호 지정을 방지하려면 어떻게 해야 합니까? (0) | 2023.06.03 |