Завдання C HTML escape / unescape


74

Цікаво, чи є простий спосіб зробити простий HTML escape / unescape в Objective C. Що я хочу, це щось на зразок цього коду psuedo:

NSString *string = @"<span>Foo</span>";
[string stringByUnescapingHTML];

Яка повертається

<span>Foo</span>

Будемо сподіватися, що всі інші HTML-сутності також будуть оброблені, а навіть коди ASCII, такі як Ӓ тощо.

Чи є в Cocoa Touch / UIKit якісь методи для цього?


Напевно, найпростіший спосіб зараз з iOS7 - використовувати здатність NSAttributedString декодувати HTML, а потім перетворити NSAttributedString на NSString - див. Мою відповідь нижче.
ордж

Відповіді:


30

Це посилання містить рішення нижче. Какао CF має функцію CFXMLCreateStringByUnescapingEntities, але ця функція недоступна на iPhone.

@interface MREntitiesConverter : NSObject <NSXMLParserDelegate>{
    NSMutableString* resultString;
}

@property (nonatomic, retain) NSMutableString* resultString;

- (NSString*)convertEntitiesInString:(NSString*)s;

@end


@implementation MREntitiesConverter

@synthesize resultString;

- (id)init
{
    if([super init]) {
        resultString = [[NSMutableString alloc] init];
    }
    return self;
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)s {
        [self.resultString appendString:s];
}

- (NSString*)convertEntitiesInString:(NSString*)s {
    if (!s) {
        NSLog(@"ERROR : Parameter string is nil");
    }
    NSString* xmlStr = [NSString stringWithFormat:@"<d>%@</d>", s];
    NSData *data = [xmlStr dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
    NSXMLParser* xmlParse = [[[NSXMLParser alloc] initWithData:data] autorelease];
    [xmlParse setDelegate:self];
    [xmlParse parse];
    return [NSString stringWithFormat:@"%@",resultString];
}

- (void)dealloc {
    [resultString release];
    [super dealloc];
}

@end

1
Чи не було б простіше застосувати це як категорію NSString, а не як цілком окремий об'єкт? Крім того, рядок, що повертається, не випускається автоматично, але абонент не повинен володіти ним, оскільки він не був явно виділений абонентом.
dreamlax

6
xmlParse також просочується до речі, просто додайте до нього автовипуск і поверніться Str
Ярін Удом

1
Якщо ви зробите це категорією NSString, вам все одно потрібен делегат парсера. Тож у будь-якому випадку вам знадобиться окремий об’єкт.
Вільям Джокуш

4
Незважаючи на те, що CFXMLCreateStringByUnescapingEntitiesвоно недоступне на iOS, ви можете скопіювати його визначення з CFXMLParser.c (із вихідного коду Core Foundation) і використовувати його у своєму проекті. Я перевірив це, і воно працює.
Чайтанья Гупта

2
Я виявив, що цей код видаляє всі теги html (наприклад, він залишив лише "Facebook" від "<a href="xxx"> Facebook </a>"), а іноді просто не повертає нічого, коли пройшов складний html. Отже, на жаль, не працює на мої цілі.
Майк Кескінов

91

Перевірте мою категорію NSString для XMLEntities . Існують методи декодування сутностей XML (включаючи всі посилання на символи HTML), кодування сутностей XML, вилучення тегів та видалення нових рядків та пробілів із рядка:

- (NSString *)stringByStrippingTags;
- (NSString *)stringByDecodingXMLEntities; // Including all HTML character references
- (NSString *)stringByEncodingXMLEntities;
- (NSString *)stringWithNewLinesAsBRs;
- (NSString *)stringByRemovingNewLinesAndWhitespace;

2
Здається, він не підтримує кирилицю. Ви бачили такий, який підтримує?
slatvick

Дякую, я вже до речі використовував ваші синтаксичні аналізи. Чудова робота!
Абрамодж

Працює як шарм. Дякуємо за цю чудову категорію!
DevZarak

9
Що з фанк-ліцензією? Не можна використовувати для щоденників та журналів?
alltom

1
Ця категорія використовує категорію Google Toolbox під капотом. Краще просто встановити помічник Google Toolbox безпосередньо через Cocoapods: pod "GTMNSStringHTMLAdditions". Див. Відповідь Тревіса від вересня 2015 р.
skensell

35

Ще одна категорія HTML NSString із Google Toolbox для Mac
Незважаючи на назву, це працює і на iOS.

http://google-toolbox-for-mac.googlecode.com/svn/trunk/Foundation/GTMNSString+HTML.h

/// Get a string where internal characters that are escaped for HTML are unescaped 
//
///  For example, '&amp;' becomes '&'
///  Handles &#32; and &#x32; cases as well
///
//  Returns:
//    Autoreleased NSString
//
- (NSString *)gtm_stringByUnescapingFromHTML;

І мені довелося включити до проекту лише три файли: заголовок, реалізація та GTMDefines.h.


2
Варто зазначити, що якщо ви шукаєте протилежне цьому, тобто '&'стає '&amp;', це також охоплюється - (NSString *)gtm_stringByEscapingForHTML;, визначеним далі у файлі.
Крістіан

Будь ласка, можете надати посилання наGTMDefines.h
Almas Adilbek

Варто зазначити, що ця категорія не сумісна з ARC, оскільки вона використовує об’єкти Objective-C у структурі, яка не підтримується. Навіть встановлення -fno-objc-arcпрапора компілятора не зупиняє позначення структури як помилки в Xcode.
robotpukeko

@robotpukeko Це дивно, тому що я зміг скомпілювати проект ARC з цією категорією, просто встановивши прапор у файл .m.
Тимур Кучкаров

просто додайте -fno-objc-arc до джерел компіляції. і це чудово працює.
Йонг Хо

29

Це неймовірно зламане рішення, яке я зробив, але якщо ви хочете просто уникнути рядка, не турбуючись про синтаксичний аналіз, зробіть так:

-(NSString *)htmlEntityDecode:(NSString *)string
    {
        string = [string stringByReplacingOccurrencesOfString:@"&quot;" withString:@"\""];
        string = [string stringByReplacingOccurrencesOfString:@"&apos;" withString:@"'"];
        string = [string stringByReplacingOccurrencesOfString:@"&lt;" withString:@"<"];
        string = [string stringByReplacingOccurrencesOfString:@"&gt;" withString:@">"];
        string = [string stringByReplacingOccurrencesOfString:@"&amp;" withString:@"&"]; // Do this last so that, e.g. @"&amp;lt;" goes to @"&lt;" not @"<"

        return string;
    }

Я знаю, що це далеко не елегантно, але це робить роботу. Потім ви можете декодувати елемент, зателефонувавши:

string = [self htmlEntityDecode:string];

Як я вже сказав, це хакі, але це працює. ЯКЩО ви хочете кодувати рядок, просто змініть параметри stringByReplacingOccurencesOfString.


5
А як щодо виступу ?? Ви проходите через рядок 5 разів. Це здається не дуже ефективним;)
HyLian

Це точно не найефективніше рішення, але воно працює. Що може бути більш ефективним способом зробити це?
Andrew Kozlik

6
Залежно від того, як часто це використовується та скільки часу ви можете фактично заощадити, зробивши це більш ефективним, тут може не мати сенсу мікрооптимізувати. Оскільки ми маємо справу з HTML тут, швидше за все, десь є мережевий запит, і повернення знадобиться в тисячі разів довше, ніж для виконання наведеного вище коду. Можливо, я схиляюся до не оптимізації цього коду.
Джош Браун,

Запропонований метод має погану продуктивність, але працює нормально, якщо вам потрібно рідко обробляти короткі рядки. Дякуємо, що заощадили час на те, що я самостійно впровадив ці 10 рядків;)
Костянтин Соколінський

@Andrew більш ефективним способом буде реалізація власного сканера рядків, який перетворить усі ці посилання на сутності XML-символів у відповідні символи в одному скануванні рядків. Складність часу впаде в 5 разів. Або ви можете використати бібліотеку, подібну запропонованій Никитою нижче - stackoverflow.com/questions/659602/…
Костянтин Соколінський

11

У iOS 7 ви можете використовувати можливість NSAttributedString імпортувати HTML для перетворення HTML-сутностей у NSString.

Наприклад:

@interface NSAttributedString (HTML)
+ (instancetype)attributedStringWithHTMLString:(NSString *)htmlString;
@end

@implementation NSAttributedString (HTML)
+ (instancetype)attributedStringWithHTMLString:(NSString *)htmlString
{
    NSDictionary *options = @{ NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType,
                               NSCharacterEncodingDocumentAttribute :@(NSUTF8StringEncoding) };

    NSData *data = [htmlString dataUsingEncoding:NSUTF8StringEncoding];

    return [[NSAttributedString alloc] initWithData:data options:options documentAttributes:nil error:nil];
}

@end

Потім у коді, коли ви хочете очистити сутності:

NSString *cleanString = [[NSAttributedString attributedStringWithHTMLString:question.title] string];

Це, мабуть, найпростіший спосіб, але я не знаю, наскільки це ефективно. Ви, мабуть, повинні бути пекельно впевнені, що вміст, який ви чистите, не містить жодних <img>тегів чи подібних речей, оскільки цей метод завантажує ці зображення під час перетворення HTML у NSAttributedString. :)


Я зробив це, написавши метод, який бере рядок, очищає його і повертає очищений рядок назад. Подивіться тут .
Адам Сімпсон

Це рішення також видаляє всі існуючі теги HTML, наприклад, залишені this is testз <b>this</b> is <a href='test'>test</a>.
Майк Кескінов

2
Просто на голову, NSAttributedString робить у конструкторі жахливі речі, як обертання циклу. Я не зміг використовувати це в основному потоці, не роблячи UIKit дуже невдоволеним.
Брайан Кінг

Це рад. Щиро дякую, працював для мене як оберіг.
Тім Джонсен,

5

Ось рішення, яке нейтралізує всі символи (зробивши їх усі HTML-кодовані сутності за значенням Unicode) ... Використовував це для моїх потреб (переконавшись, що рядок, який надійшов від користувача, але був розміщений всередині веб-перегляду, не міг мати жодного Атаки XSS):

Інтерфейс:

@interface NSString (escape)
- (NSString*)stringByEncodingHTMLEntities;
@end

Реалізація:

@implementation NSString (escape)

- (NSString*)stringByEncodingHTMLEntities {
    // Rather then mapping each individual entity and checking if it needs to be replaced, we simply replace every character with the hex entity

    NSMutableString *resultString = [NSMutableString string];
    for(int pos = 0; pos<[self length]; pos++)
        [resultString appendFormat:@"&#x%x;",[self characterAtIndex:pos]];
    return [NSString stringWithString:resultString];
}

@end

Приклад використання:

UIWebView *webView = [[UIWebView alloc] init];
NSString *userInput = @"<script>alert('This is an XSS ATTACK!');</script>";
NSString *safeInput = [userInput stringByEncodingHTMLEntities];
[webView loadHTMLString:safeInput baseURL:nil];

Ваш пробіг буде змінюватися.


Вам не вистачає символу ';' наприкінці послідовності екранування, також, у всіх документах я знайшов, що довжина номера Unicode дорівнює 4 із початковими нулями, тому ваш формат повинен бути @"&#x%04x;", крім цього, я б додав простий алфавітно-цифровий детектор і просто скопіював таких персонажів, не рятуючись.
Моше Готліб

Цікаво, що цей код добре працює для мене без крапки з комою. Можливо, просто вебкіт надійний. Я це додав. Однак не робіть% 04x, як пропонується, інакше у вас можуть виникнути проблеми з однобайтовими багатобайтовими символами Unicode. Використання% x друкує правильне число як для однобайтного, так і для багатобайтового (наприклад, японського).
BadPirate

4

Найменш інвазивним і найлегшим способом кодування та декодування рядків HTML або XML є використання GTMNSStringHTMLAdditions CocoaPod .

Це просто категорія інструментів Google для Mac NSString GTMNSString+HTML, позбавлена ​​залежності від GTMDefines.h. Отже, все, що вам потрібно додати, - це один .h та один .m, і ви готові піти.

Приклад:

#import "GTMNSString+HTML.h"

// Encoding a string with XML / HTML elements
NSString *stringToEncode = @"<TheBeat>Goes On</TheBeat>";
NSString *encodedString = [stringToEncode gtm_stringByEscapingForHTML];

// encodedString looks like this now:
// &lt;TheBeat&gt;Goes On&lt;/TheBeat&gt;

// Decoding a string with XML / HTML encoded elements
NSString *stringToDecode = @"&lt;TheBeat&gt;Goes On&lt;/TheBeat&gt;";
NSString *decodedString = [stringToDecode gtm_stringByUnescapingFromHTML];

// decodedString looks like this now:
// <TheBeat>Goes On</TheBeat>

2

Це проста у використанні реалізація категорії NSString:

Це далеко не повне, але ви можете додати деякі відсутні елементи тут: http://code.google.com/p/statz/source/browse/trunk/NSString%2BHTML.m

Використання:

#import "NSString+HTML.h"

NSString *raw = [NSString stringWithFormat:@"<div></div>"];
NSString *escaped = [raw htmlEscapedString];

Я можу підтвердити, що ця категорія працює ідеально. Це чудово написано. Я закликаю всіх використовувати його - я сумніваюся, що там є краще рішення! Знову ж таки дивно, це ще не вбудовано в iOS .. bizarro. Завдяки @blago
Fattie




0

Найпростішим рішенням є створення категорії, як показано нижче:

Ось файл заголовка категорії:

#import <Foundation/Foundation.h>
@interface NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding;
@end

І ось реалізація:

#import "NSString+URLEncoding.h"
@implementation NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
    return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
               (CFStringRef)self,
               NULL,
               (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
               CFStringConvertNSStringEncodingToEncoding(encoding));
}
@end

І тепер ми можемо просто зробити це:

NSString *raw = @"hell & brimstone + earthly/delight";
NSString *url = [NSString stringWithFormat:@"http://example.com/example?param=%@",
            [raw urlEncodeUsingEncoding:NSUTF8Encoding]];
NSLog(url);

Кредити за цю відповідь надходять на веб-сайт нижче: -

http://madebymany.com/blog/url-encoding-an-nsstring-on-ios

Це кодування URL-адреси. Питання полягає в тому, що HTML не вдається уникнути, а не кодування URL-адреси.
Тім Джонсен

-4

Чому не просто використовувати?

NSData *data = [s dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSString *result = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
return result;

Noob питання, але в моєму випадку це працює ...


1
Чому б це працювало? Наскільки я можу сказати, він просто перетворюється у двійкові дані, а потім повертається у рядок. Я не розумію, що тут перетворило б ">" на "& gt;" і навпаки.
Alex Wayne

-5

Це стара відповідь, яку я опублікував кілька років тому. Я мав намір не запропонувати "гарне" та "респектабельне" рішення, а "хакі", яке може бути корисним за певних обставин. Будь ласка, не використовуйте це рішення, якщо ніщо інше не працює.

Насправді це чудово працює в багатьох ситуаціях, а інші відповіді не роблять, оскільки UIWebView виконує всю роботу. І ви навіть можете ввести якийсь javascript (що може бути небезпечним та / або корисним). Виступ повинен бути жахливим, але насправді не так вже й поганий.

Існує ще одне рішення, яке слід згадати. Просто створіть a UIWebView, завантажте закодований рядок і поверніть текст назад. Він уникає тегів "<>", а також декодує всі HTML-сутності (наприклад, "& gt;"), і він може працювати там, де інших немає (наприклад, за допомогою кирилиці). Я не думаю, що це найкраще рішення, але воно може бути корисним, якщо вищезазначені рішення не працюють.

Ось невеликий приклад використання ARC:

@interface YourClass() <UIWebViewDelegate>

    @property UIWebView *webView;

@end

@implementation YourClass 

- (void)someMethodWhereYouGetTheHtmlString:(NSString *)htmlString {
    self.webView = [[UIWebView alloc] init];
    NSString *htmlString = [NSString stringWithFormat:@"<html><body>%@</body></html>", self.description];
    [self.webView loadHTMLString:htmlString baseURL:nil];
    self.webView.delegate = self;
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    self.webView = nil;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    self.webView = nil;
    NSString *escapedString = [self.webView stringByEvaluatingJavaScriptFromString:@"document.body.textContent;"];
}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    // Do Nothing
}

@end

сарказм, я думаю, це великий показник і ресурси / сарказм
dreamlab
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.