Видаліть HTML-теги з NSString на iPhone


106

Є кілька різних способів видалення HTML tagsз NSStringдюйма Cocoa.

Один із способів - перетворити рядок у, NSAttributedStringа потім захопити виведений текст.

Інший спосіб - використовувати NSXMLDocument's- objectByApplyingXSLTStringметод застосувати XSLTперетворення, яке це робить.

На жаль, iPhone не підтримує NSAttributedStringабо NSXMLDocument. Для HTMLмене занадто багато крайових справ і неправильно оформлених документів, щоб відчувати себе комфортно за допомогою регулярного виразу або NSScanner. Хтось має на це рішення?

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

Наприклад, ці випадки (з розділу Perl Cookbook з тієї ж теми) порушили цей метод:

<IMG SRC = "foo.gif" ALT = "A > B">

<!-- <A comment> -->

<script>if (a<b && a>c)</script>

<![INCLUDE CDATA [ >>>>>>>>>>>> ]]>

Ви можете додати трохи логіки, щоб взяти до уваги цитати та апострофи ... CDATA потребує трохи більше роботи, але вся суть HTML полягає в тому, що невідомі теги можуть бути проігноровані аналізатором; якщо ви ставитесь до ВСІХ тегів як до невідомих, то вам слід просто отримати необроблений текст.
Бен Готліб

Я хотів би прокоментувати, що хороший (але базовий) регулярний вираз напевно не порушить ваші приклади. Звичайно ні, якщо ви можете гарантувати добре сформовану XHTML. Я знаю, що ви сказали, що не можете, але мені цікаво, чому ;-)
Джейк

1
На це питання є хороша відповідь . Вирівняти HTML за допомогою Objective c
vipintj

На жаль, використання NSScanner проклято повільно.
steipete

Навіть більше, на жаль, зв'язаний приклад NSScanner працює лише для тривіального html. Це не вдається для кожного тестового випадку, про який я згадував у своєму дописі.
lfalin

Відповіді:


309

Швидке і "брудне" (видаляє все між <і>) рішення, працює з iOS> = 3.2:

-(NSString *) stringByStrippingHTML {
  NSRange r;
  NSString *s = [[self copy] autorelease];
  while ((r = [s rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
    s = [s stringByReplacingCharactersInRange:r withString:@""];
  return s;
}

У мене це оголошено як категорію OS NSString.


4
@James Щоб використовувати метод, розміщений у рішенні. Ви повинні створити категорію для NSString. Подивіться "Категорію Objective-C" в Google. Потім ви додаєте цей метод у m-файл, а прототип - у h-файл. Коли це все налаштовано, для використання всього, що вам потрібно зробити, є об’єкт рядка (Приклад: NSString * myString = ...), і ви викликаєте цей метод на своєму рядковому об'єкті (NSString * strippedString = [myString stringByStrippingHTML]; ).
Роберто

3
+1 Прекрасне використання для регулярних виразів, але, на жаль, не охоплює багато випадків.
математика

3
Швидкий і брудний дійсно .... Ця функція спричиняє величезну протікання пам'яті у моєму додатку ... Ну, на захист я використовую великі обсяги даних ....
EZFrag

5
У моєму додатку це рішення викликало проблеми з продуктивністю. Я перейшов до рішення з NSScanner замість NSRegularExpressionSearch. Зараз проблем з продуктивністю вже немає
carmen_munich

2
Це дуже-дуже пам'ять і забирає багато часу. Використовуйте це лише з невеликою кількістю html!
ульстрм

29

У цій NSStringкатегорії використовується NSXMLParserточне видалення будь-яких HTMLтегів із NSString. Це єдиний .mі.h файл, який можна легко включити у ваш проект.

https://gist.github.com/leighmcculloch/1202238

Потім ви зніміть html , виконуючи наступні дії:

Імпортуйте заголовок:

#import "NSString_stripHtml.h"

А потім зателефонуйте stripHtml:

NSString* mystring = @"<b>Hello</b> World!!";
NSString* stripped = [mystring stripHtml];
// stripped will be = Hello World!!

Це також працює з неправильним формуванням, HTMLяке технічно не є XML.


3
Хоча регулярний вираз (як говорив m.kocikowski) швидкий і брудний, це більш надійно. Приклад рядка: @ "Мій тест <span font = \" font> name \ "> html string". Ця відповідь повертає: Мій тестовий HTML-рядок. Регулярний вираз повертається: My test name "> html string. Хоча це не так часто, він просто більш надійний.
DonnaLea

1
За винятком того, що у вас є рядок типу "S&P 500", вона зніме все після амперсанда і просто поверне рядок "S".
Джошуа Гросс

11
UITextView *textview= [[UITextView alloc]initWithFrame:CGRectMake(10, 130, 250, 170)];
NSString *str = @"This is <font color='red'>simple</font>";
[textview setValue:str forKey:@"contentToHTMLString"];
textview.textAlignment = NSTextAlignmentLeft;
textview.editable = NO;
textview.font = [UIFont fontWithName:@"vardana" size:20.0];
[UIView addSubview:textview];

мені добре працювати


1
У мене виникла проблема кодування з цим рішенням
KIDdAe,

Можливо , найкраще рішення, але це марно для UILabel :-(
Зеб

9

Ви можете використовувати як нижче

-(void)myMethod
 {

 NSString* htmlStr = @"<some>html</string>";
 NSString* strWithoutFormatting = [self stringByStrippingHTML:htmlStr];

 }

 -(NSString *)stringByStrippingHTML:(NSString*)str
 {
   NSRange r;
   while ((r = [str rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location     != NSNotFound)
  {
     str = [str stringByReplacingCharactersInRange:r withString:@""];
 }
  return str;
 }

8

використовуй це

NSString *myregex = @"<[^>]*>"; //regex to remove any html tag

NSString *htmlString = @"<html>bla bla</html>";
NSString *stringWithoutHTML = [hstmString stringByReplacingOccurrencesOfRegex:myregex withString:@""];

не забудьте включити це у свій код: #import "RegexKitLite.h" ось посилання для завантаження цього API: http://regexkit.sourceforge.net/#Downloads


7

Погляньте на NSXMLParser. Це аналізатор стилю SAX. Ви повинні мати можливість використовувати його для виявлення тегів чи інших небажаних елементів у документі XML та ігнорування їх, захоплюючи лише чистий текст.


6

Ось більш ефективне рішення, ніж прийнята відповідь:

- (NSString*)hp_stringByRemovingTags
{
    static NSRegularExpression *regex = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        regex = [NSRegularExpression regularExpressionWithPattern:@"<[^>]+>" options:kNilOptions error:nil];
    });

    // Use reverse enumerator to delete characters without affecting indexes
    NSArray *matches =[regex matchesInString:self options:kNilOptions range:NSMakeRange(0, self.length)];
    NSEnumerator *enumerator = matches.reverseObjectEnumerator;

    NSTextCheckingResult *match = nil;
    NSMutableString *modifiedString = self.mutableCopy;
    while ((match = [enumerator nextObject]))
    {
        [modifiedString deleteCharactersInRange:match.range];
    }
    return modifiedString;
}

Вищенаведена NSStringкатегорія використовує регулярний вираз для пошуку всіх відповідних тегів, робить копію оригінального рядка і, нарешті, видаляє всі теги на місці, повторюючи їх у зворотному порядку. Це більш ефективно, оскільки:

  • Регулярний вираз ініціалізується лише один раз.
  • Використовується одна копія оригінального рядка.

Це було досить добре для мене, але за допомогою рішення NSScanner може бути більш ефективним.

Як і прийнята відповідь, це рішення не стосується всіх прикордонних випадків, які вимагає @lfalin. Для них знадобиться набагато дорожчий аналіз, який середній випадок використання, швидше за все, не потребує.


5

Без циклу (принаймні з нашого боку):

- (NSString *)removeHTML {

    static NSRegularExpression *regexp;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        regexp = [NSRegularExpression regularExpressionWithPattern:@"<[^>]+>" options:kNilOptions error:nil];
    });

    return [regexp stringByReplacingMatchesInString:self
                                            options:kNilOptions
                                              range:NSMakeRange(0, self.length)
                                       withTemplate:@""];
}

Це має бути прийнятою відповіддю. Нинішній смішно марнотратний.
Адлай Холлер

5
NSAttributedString *str=[[NSAttributedString alloc] initWithData:[trimmedString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil];

Коли у нас є метадані з тегами HTML і хоче застосувати ці теги, тоді ми повинні застосувати вищезазначений код, щоб досягти результату бажання.
Pavan Sisode


3

Я продовжив відповідь m.kocikowski і намагався зробити її трохи ефективнішою, використовуючи NSMutableString. Я також структурував його для використання в статичному класі Utils (я знаю, що категорія, мабуть, найкращий дизайн), і видалив автовипуск, тому він компілюється в проект ARC.

Тут включено, якщо хтось вважає це корисним.

.h

+ (NSString *)stringByStrippingHTML:(NSString *)inputString;

.m

+ (NSString *)stringByStrippingHTML:(NSString *)inputString 
{
  NSMutableString *outString;

  if (inputString)
  {
    outString = [[NSMutableString alloc] initWithString:inputString];

    if ([inputString length] > 0)
    {
      NSRange r;

      while ((r = [outString rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
      {
        [outString deleteCharactersInRange:r];
      }      
    }
  }

  return outString; 
}

Цей метод корисний, але, якщо мені потрібно скасувати тег, наприклад, посилання <a>, хто я можу оновити цей метод, щоб виконати це
wod

@wod тоді просто змініть регулярний вираз, щоб <(?>/?)(?!a).+?>видалити всі теги, за винятком відкриваючих тегів <a> та закриття </a>.
Ashoor

3

Якщо ви хочете отримати вміст без тегів html з веб-сторінки (HTML-документ), використовуйте цей код у методі UIWebViewDidfinishLoading делегування .

  NSString *myText = [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.textContent"];

<br> замінюється нічим ... що небажано.
Нішант

2

Я думаю, що найбезпечнішим способом було б просто розібратися на <> s, ні? Проведіть цикл через увесь рядок і скопіюйте все, що не вкладено у <> s, у новий рядок.


2

Це модернізація відповіді m.kocikowski, яка видаляє пробіли:

@implementation NSString (StripXMLTags)

- (NSString *)stripXMLTags
{
    NSRange r;
    NSString *s = [self copy];
    while ((r = [s rangeOfString:@"<[^>]+>\\s*" options:NSRegularExpressionSearch]).location != NSNotFound)
        s = [s stringByReplacingCharactersInRange:r withString:@""];
    return s;
}

@end

2

Далі йде прийнята відповідь, але замість категорії це простий хелперний метод із переданою в нього рядком. (дякую m.kocikowski)

-(NSString *) stringByStrippingHTML:(NSString*)originalString {
    NSRange r;
    NSString *s = [originalString copy];
    while ((r = [s rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
        s = [s stringByReplacingCharactersInRange:r withString:@""];
    return s;
}

2

Ось швидка версія:

func stripHTMLFromString(string: String) -> String {
  var copy = string
  while let range = copy.rangeOfString("<[^>]+>", options: .RegularExpressionSearch) {
    copy = copy.stringByReplacingCharactersInRange(range, withString: "")
  }
  copy = copy.stringByReplacingOccurrencesOfString("&nbsp;", withString: " ")
  copy = copy.stringByReplacingOccurrencesOfString("&amp;", withString: "&")
  return copy
}

Людина, що stringByReplacingOccurrencesOfStringвикористовуєшся поза циклом, - це відсоткове кодування і має бути виправлене правильним способом.
Вячаслав Герчич

0

Якщо ви готові використовувати рамку Three20 , вона містить категорію на NSString, яка додає метод stringByRemovingHTMLTags. Дивіться NSStringAdditions.h у підпроект Three20Core.


26
Заради бога, не використовуйте Three20 ні для чого. Найбільш роздуті та погано коментовані рамки коли-небудь.
композитор

0

Продовжуючи це ще з відповідей м.коціковського та Ден Дж із більшою поясненням для новачків

1 # Спочатку вам потрібно створити цілі-c-категорії, щоб зробити код корисним у будь-якому класі.

.h

@interface NSString (NAME_OF_CATEGORY)

- (NSString *)stringByStrippingHTML;

@end

.m

@implementation NSString (NAME_OF_CATEGORY)

- (NSString *)stringByStrippingHTML
{
NSMutableString *outString;
NSString *inputString = self;

if (inputString)
{
    outString = [[NSMutableString alloc] initWithString:inputString];

    if ([inputString length] > 0)
    {
        NSRange r;

        while ((r = [outString rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
        {
            [outString deleteCharactersInRange:r];
        }
    }
}

return outString;
}

@end

2 # Тоді просто імпортуйте .h файл категорій класу, який ви створили, наприклад

#import "NSString+NAME_OF_CATEGORY.h"

3 # Виклик методу.

NSString* sub = [result stringByStrippingHTML];
NSLog(@"%@", sub);

Результатом є NSString, з якого я хочу позбавити теги.


0

Я дотримуючись прийнятої відповіді m.kocikowski, і модифікація трохи використати авторелепульд для очищення всіх тимчасових рядків, які створюються stringByReplacingCharactersInRange

У коментарі до цього методу зазначено, / * Замініть символи в діапазоні на вказану рядок, повертаючи нову рядок. * /

Отже, залежно від довжини вашого XML, ви можете створювати величезну купу нових рядків автовипуску, які не очищаються до кінця наступного @autoreleasepool. Якщо ви не впевнені, коли це може статися, або якщо дія користувача може неодноразово викликати багато дзвінків до цього методу, тоді ви можете просто перегорнути його в @autoreleasepool. Вони навіть можуть бути вкладені та використані в циклах, де це можливо.

Посилання Apple на @autoreleasepool говорить про це ... "Якщо ви пишете цикл, який створює багато тимчасових об'єктів. Ви можете використовувати блок пулу автоматичного випуску всередині циклу, щоб розпоряджатися цими об'єктами до наступної ітерації. Використання блоку пулу авторелізи в циклі допомагає зменшити максимальний слід пам'яті програми. " Я не використовував це в циклі, але принаймні цей метод очищає після себе зараз.

- (NSString *) stringByStrippingHTML {
    NSString *retVal;
    @autoreleasepool {
        NSRange r;
        NSString *s = [[self copy] autorelease];
        while ((r = [s rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound) {
            s = [s stringByReplacingCharactersInRange:r withString:@""];
        }
        retVal = [s copy];
    } 
    // pool is drained, release s and all temp 
    // strings created by stringByReplacingCharactersInRange
    return retVal;
}

0

Ще один спосіб:

Інтерфейс:

-(NSString *) stringByStrippingHTML:(NSString*)inputString;

Впровадження

(NSString *) stringByStrippingHTML:(NSString*)inputString
{ 
NSAttributedString *attrString = [[NSAttributedString alloc] initWithData:[inputString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} documentAttributes:nil error:nil];
NSString *str= [attrString string]; 

//you can add here replacements as your needs:
    [str stringByReplacingOccurrencesOfString:@"[" withString:@""];
    [str stringByReplacingOccurrencesOfString:@"]" withString:@""];
    [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];

    return str;
}

Реалізація

cell.exampleClass.text = [self stringByStrippingHTML:[exampleJSONParsingArray valueForKey: @"key"]];

або просто

NSString *myClearStr = [self stringByStrippingHTML:rudeStr];


цей метод видалення html tags.but я хочу розібрати html string. Що робити
Krutarth Patel

врятував мій час.nice рішення
Krutarth Patel

0

Оновлена ​​відповідь для @ m.kocikowski, яка працює на останніх версіях iOS.

-(NSString *) stringByStrippingHTMLFromString:(NSString *)str {
NSRange range;
while ((range = [str rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
    str = [str stringByReplacingCharactersInRange:range withString:@""];
return str;

}


-3

Ось публікація в блозі, де обговорюється пара бібліотек, доступних для зняття HTML http://sugarmaplesoftware.com/25/strip-html-tags/ Зверніть увагу на коментарі, де пропонуються інші рішення.


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