Кодировать NSString для XML/HTML
-
03-07-2019 - |
Вопрос
Есть ли способ HTML-кодирования строки (NSString) в Objective-C, что-то вроде Server.HtmlEncode в .NET?
Решение
Не существует метода NSString, который бы это делал.Вам придется написать свою собственную функцию, выполняющую замену строк.Достаточно сделать следующие замены:
- '&' => "&"
- '"' => """
- '\'' => "'"
- '>' => ">"
- '<' => "<"
Должно получиться что-то вроде этого (не пробовал):
[[[[[myStr stringByReplacingOccurrencesOfString: @"&" withString: @"&"]
stringByReplacingOccurrencesOfString: @"\"" withString: @"""]
stringByReplacingOccurrencesOfString: @"'" withString: @"'"]
stringByReplacingOccurrencesOfString: @">" withString: @">"]
stringByReplacingOccurrencesOfString: @"<" withString: @"<"];
Другие советы
Я взял работу Майка и превратил ее в категорию для NSMutableString и NSString.
Создайте категорию для NSmutableString с помощью:
- (NSMutableString *)xmlSimpleUnescape
{
[self replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@""" withString:@"\"" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"'" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"'" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"’" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"–" withString:@"-" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
return self;
}
- (NSMutableString *)xmlSimpleEscape
{
[self replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"\"" withString:@""" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"'" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
[self replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [self length])];
return self;
}
Создайте категорию для NSString с помощью:
- (NSString *)xmlSimpleUnescapeString
{
NSMutableString *unescapeStr = [NSMutableString stringWithString:self];
return [unescapeStr xmlSimpleUnescape];
}
- (NSString *)xmlSimpleEscapeString
{
NSMutableString *escapeStr = [NSMutableString stringWithString:self];
return [escapeStr xmlSimpleEscape];
}
* Версия Swift 2.0 *
Версия Objective-C немного более эффективна, поскольку она выполняет изменяемые операции со строкой.Однако это быстрый способ выполнить простое экранирование:
extension String
{
typealias SimpleToFromRepalceList = [(fromSubString:String,toSubString:String)]
// See http://stackoverflow.com/questions/24200888/any-way-to-replace-characters-on-swift-string
//
func simpleReplace( mapList:SimpleToFromRepalceList ) -> String
{
var string = self
for (fromStr, toStr) in mapList {
let separatedList = string.componentsSeparatedByString(fromStr)
if separatedList.count > 1 {
string = separatedList.joinWithSeparator(toStr)
}
}
return string
}
func xmlSimpleUnescape() -> String
{
let mapList : SimpleToFromRepalceList = [
("&", "&"),
(""", "\""),
("'", "'"),
("'", "'"),
("’", "'"),
("–", "-"),
(">", ">"),
("<", "<")]
return self.simpleReplace(mapList)
}
func xmlSimpleEscape() -> String
{
let mapList : SimpleToFromRepalceList = [
("&", "&"),
("\"", """),
("'", "'"),
(">", ">"),
("<", "<")]
return self.simpleReplace(mapList)
}
}
Я мог бы использовать возможности моста NSString, чтобы написать что-то очень похожее на версию NSString, но я решил сделать это быстрее.
я использую Панель инструментов Google для Mac (работает на iPhone).В частности, см. дополнения к NSString в GTMNSString+HTML.h и GTMNSString+XML.h.
Для кодирования URL:
NSString * encodedString = [originalString
stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
Видеть Документация Apple NSString для получения дополнительной информации.
Для HTML-кодирования:
Проверить CFXMLCreateStringByEscapingEntities, который является частью библиотеки Core Foundation XML, но все равно должен помочь.
Программа Саметса забыла шестнадцатеричную цифру.Вот процедура, которую я придумал, которая работает:
- (NSString*)convertEntities:(NSString*)string
{
NSString *returnStr = nil;
if( string )
{
returnStr = [ string stringByReplacingOccurrencesOfString:@"&" withString: @"&" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@""" withString:@"\"" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"'" withString:@"'" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"9" withString:@"'" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"’" withString:@"'" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"–" withString:@"'" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@">" withString:@">" ];
returnStr = [ returnStr stringByReplacingOccurrencesOfString:@"<" withString:@"<" ];
returnStr = [ [ NSString alloc ] initWithString:returnStr ];
}
return returnStr;
}
Вот более эффективная реализация этой управляющей логики XML.
+ (NSString*) xmlSimpleEscape:(NSString*)unescapedStr
{
if (unescapedStr == nil || [unescapedStr length] == 0) {
return unescapedStr;
}
const int len = [unescapedStr length];
int longer = ((int) (len * 0.10));
if (longer < 5) {
longer = 5;
}
longer = len + longer;
NSMutableString *mStr = [NSMutableString stringWithCapacity:longer];
NSRange subrange;
subrange.location = 0;
subrange.length = 0;
for (int i = 0; i < len; i++) {
char c = [unescapedStr characterAtIndex:i];
NSString *replaceWithStr = nil;
if (c == '\"')
{
replaceWithStr = @""";
}
else if (c == '\'')
{
replaceWithStr = @"'";
}
else if (c == '<')
{
replaceWithStr = @"<";
}
else if (c == '>')
{
replaceWithStr = @">";
}
else if (c == '&')
{
replaceWithStr = @"&";
}
if (replaceWithStr == nil) {
// The current character is not an XML escape character, increase subrange length
subrange.length += 1;
} else {
// The current character will be replaced, but append any pending substring first
if (subrange.length > 0) {
NSString *substring = [unescapedStr substringWithRange:subrange];
[mStr appendString:substring];
}
[mStr appendString:replaceWithStr];
subrange.location = i + 1;
subrange.length = 0;
}
}
// Got to end of unescapedStr so append any pending substring, in the
// case of no escape characters this will append the whole string.
if (subrange.length > 0) {
if (subrange.location == 0) {
[mStr appendString:unescapedStr];
} else {
NSString *substring = [unescapedStr substringWithRange:subrange];
[mStr appendString:substring];
}
}
return [NSString stringWithString:mStr];
}
+ (NSString*) formatSimpleNode:(NSString*)tagname value:(NSString*)value
{
NSAssert(tagname != nil, @"tagname is nil");
NSAssert([tagname length] > 0, @"tagname is the empty string");
if (value == nil || [value length] == 0) {
// Certain XML parsers don't like empty nodes like "<foo/>", use "<foo />" instead
return [NSString stringWithFormat:@"<%@ />", tagname];
} else {
NSString *escapedValue = [self xmlSimpleEscape:value];
return [NSString stringWithFormat:@"<%@>%@</%@>", tagname, escapedValue, tagname];
}
}
Если вы можете использовать NSXMLNode (в OS X), вот в чем хитрость:
NSString *string = @"test<me>"
NSXMLNode *textNode = [NSXMLNode textWithStringValue:string];
NSString *escapedString = [textNode.XMLString];
Вот моя быстрая категория для кодирования/декодирования HTML:
extension String
{
static let htmlEscapedDictionary = [
"&": "&",
""" : "\"",
"'" : "'",
"9" : "'",
"’" : "'",
"–" : "'",
">" : ">",
"<" : "<"]
var escapedHtmlString : String {
var newString = "\(self)"
for (key, value) in String.htmlEscapedDictionary {
newString.replace(value, withString: key)
}
return newString
}
var unescapedHtmlString : String {
let encodedData = self.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions : [String: AnyObject] = [
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
]
let attributedString = NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil, error: nil)!
return attributedString.string
}
mutating func replace(originalString:String, withString newString:String)
{
let replacedString = self.stringByReplacingOccurrencesOfString(originalString, withString: newString, options: nil, range: nil)
self = replacedString
}
}
Я предполагаю, что обратную сторону htmlEscapedDictionary можно было бы использовать и в unescapedHtmlString.
Примечание: Как отметил МаркБау в комментарии ниже:Поскольку Swift не гарантирует порядок словарей, обязательно замените &
первый.
Я не совсем уверен, будет ли это работать во всех случаях, но, возможно, проще окружить текст CDATA:
<xmltag><![CDATA[some <b>long</b> <i>xml</i> text]]></xmltag>
что такое CDATA:Что означает <![CDATA[]]> в XML?
Я собрал небольшой пример проекта, используя ответы Майка и Тода. здесь.
Делает кодирование/декодирование простым:
NSString *html = @"<p>This \"paragraph\" contains quoted & 'single' quoted stuff.</p>";
NSLog(@"Original String: %@", html);
NSString *escapedHTML = [html xmlSimpleEscapeString];
NSLog(@"Escaped String: %@", escapedHTML);
NSString *unescapedHTML = [escapedHTML xmlSimpleUnescapeString];
NSLog(@"Unescaped String: %@", unescapedHTML);
Свифт 4
extension String {
var xmlEscaped: String {
return replacingOccurrences(of: "&", with: "&")
.replacingOccurrences(of: "\"", with: """)
.replacingOccurrences(of: "'", with: "'")
.replacingOccurrences(of: ">", with: ">")
.replacingOccurrences(of: "<", with: "<")
}
}
Самое простое решение — создать категорию, как показано ниже:
Вот заголовок категории:
#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:NSUTF8StringEncoding]];
NSLog(url);
Кредиты за этот ответ передаются на веб-сайт ниже: -
http://madebymany.com/blog/url-encoding-an-nsstring-on-ios
См. ответ ниже:
NSString *content = global.strPrivacyPolicy;
content = [[[[[content stringByReplacingOccurrencesOfString: @"&" withString: @"&"]
stringByReplacingOccurrencesOfString:@""" withString:@"\" "]
stringByReplacingOccurrencesOfString: @"'" withString:@"'"]
stringByReplacingOccurrencesOfString: @">" withString: @">"]
stringByReplacingOccurrencesOfString: @"<" withString:@"<"];
[_webViewPrivacy loadHTMLString:content baseURL:nil];
Используйте сообщение в примере ниже:
anyStringConverted = [anyString stringByReplacingOccurrencesOfString:@"\n" withString:@"<br>"];
Это преобразует команду «новая строка» в соответствующий HTML-код.Но для конвертации символов вам придется написать соответствующий html-номер.Полный список HTML-номеров можно увидеть здесь.
Я нашел единственный способ, который использует только встроенные функции (а не ручной анализ) и охватывает все случаи.В дополнение к Foundation требуется AppKit/UIKit.Это Swift, но его легко можно использовать как Objective-C:
func encodedForHTML() -> String {
// make a plain attributed string and then use its HTML write functionality
let attrStr = NSAttributedString(string: self)
// by default, the document outputs a whole HTML element
// warning: if default apple implementation changes, this may need to be tweaked
let options: [NSAttributedString.DocumentAttributeKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.excludedElements: [
"html",
"head",
"meta",
"title",
"style",
"p",
"body",
"font",
"span"
]
]
// generate data and turn into string
let data = try! attrStr.data(from: NSRange(location: 0, length: attrStr.length), documentAttributes: options)
let str = String(data: data, encoding: .utf8)!
// remove <?xml line
return str.components(separatedBy: .newlines).dropFirst().first!
}