Удалите 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 09

К сожалению, использование NSScanner чертовски медленно.
steipete

К большому сожалению, связанный пример NSScanner работает только для тривиального HTML. Он терпит неудачу для каждого тестового примера, который я упомянул в своем сообщении.
lfalin 02

Ответы:


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. Найдите в Google "категорию Objective-C". Затем вы добавляете этот метод в файл m и прототип в файл h. Когда все это настроено, для его использования все, что вам нужно сделать, это иметь строковый объект (пример: NSString * myString = ...), и вы вызываете этот метод для своего строкового объекта (NSString * strippedString = [myString stringByStrippingHTML]; ).
Роберто

3
+1 Отличное применение для регулярных выражений, но, к сожалению, не во многих случаях.
matm

3
Действительно быстро и грязно .... Эта функция вызывает огромную утечку памяти в моем приложении ... Что ж, в качестве защиты я использую большие объемы данных ....
EZFrag 09

5
В моем приложении это решение вызвало проблемы с производительностью. Я перешел на решение с NSScanner вместо NSRegularExpressionSearch. Теперь проблемы с производительностью ушли
carmen_munich

2
Это очень, очень много памяти и требует много времени. Используйте это только с небольшими объемами HTML!
ullstrm 02

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. Хотя это не так часто, но просто более
надежно

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-тегами и мы хотим применить эти теги, тогда мы должны применить приведенный выше код, чтобы получить желаемый результат.
Паван Сисоде,

4
#import "RegexKitLite.h"

string text = [html stringByReplacingOccurrencesOfRegex:@"<[^>]+>" withString:@""]

2
HTML не является обычным языком, поэтому вам не следует пытаться разбирать / удалять его с помощью регулярного выражения. stackoverflow.com/questions/1732348/…
csaunders 07

3

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

Включено сюда на случай, если кому-то это пригодится.

.час

+ (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> ничем не заменяется ... что нежелательно.
Nishant

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 ни для чего. Самый раздутый и плохо комментируемый фреймворк.
kompozer

0

Расширение этого еще из ответов m.kocikowski и Dan J с дополнительными объяснениями для новичков

1 # Сначала вам нужно создать объектные категории, чтобы код можно было использовать в любом классе.

.час

@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. но я хочу проанализировать строку html. что делать
Крутарт Патель

сэкономил мое время. красивое решение
Крутарт Патель

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.