iPhoneのNSStringからHTMLタグを削除する


106

削除するにはいくつかの異なる方法があるHTML tagsからNSStringではCocoa

1つの方法は、文字列をにレンダリングしNSAttributedString、レンダリングされたテキストを取得することです。

別の方法は、NSXMLDocument's- objectByApplyingXSLTStringメソッドを使用XSLTして、それを行う変換を適用することです。

残念ながら、iPhoneはNSAttributedStringまたはをサポートしていませんNSXMLDocumentHTML正規表現またはを使用して快適に感じるには、エッジケースと不正なドキュメントが多すぎNSScannerます。誰かがこれに対する解決策を持っていますか?

開始タグと終了タグの文字を単純に探すという提案が1つありました。この方法は、ごく簡単な場合を除いて機能しません。

たとえば、これらのケース(同じ主題のPerlクックブックの章にある)は、このメソッドを壊します。

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

<!-- <A comment> -->

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

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

引用符とアポストロフィを考慮に入れるために少しロジックを追加することができます... CDATAはもう少し作業が必要ですが、HTMLの重要な点は、不明なタグはパーサーによって無視できるということです。すべてのタグを不明として扱う場合は、そのままのテキストを取得する必要があります。
ベンゴットリーブ

良い(しかし基本的な)正規表現が間違いなくあなたの例で壊れないことをコメントしたいと思います。確かに、整形式のXHTMLを保証できる場合はそうではありません。私はあなたができないと言ったのを知っていますが、なぜだろうと思います;-)
ジェイク

1
この質問には良い答えがあります。Objective cを使用してHTMLをフラット化する
vipintj

残念ながら、NSScannerの使用は非常に遅いです。
steipete

さらに残念なことに、リンクされたNSScannerの例は、些細なhtmlに対してのみ機能します。私の投稿で言及したすべてのテストケースで失敗します。
lfalin 2013年

回答:


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
確かに
速くて

5
私のアプリでは、このソリューションによりパフォーマンスの問題が発生しました。NSRegularExpressionSearchの代わりにNSScannerを使用するソリューションに切り替えました。パフォーマンスの問題がなくなりました
carmen_munich

2
それは非常に非常に非常にメモリと時間がかかります。これは少量のhtmlでのみ使用してください!
ullstrm 14

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で述べたとおり)は速くて汚いですが、これはより堅牢です。文字列の例:@ "My test <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には役に立たないでしょう:-(
Zeb

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カテゴリでは、正規表現を使用して一致するすべてのタグを検索し、元の文字列のコピーを作成して、最後にそれらを逆の順序で反復することによってすべてのタグを削除します。次の理由により、より効率的です。

  • 正規表現は一度だけ初期化されます。
  • 元の文字列の1つのコピーが使用されます。

これは十分に機能しましたが、使用するソリューションの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:@""];
}

これは受け入れられる答えになるはずです。現在のものは途方もなく無駄です。
Adlai Holler

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

HTMLタグ付きのメタデータがあり、そのタグを適用したい場合は、そのときに上記のコードを適用して、望ましい出力を得る必要があります。
Pavan Sisode 2015年


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 2013

3

HTMLタグなしのコンテンツをWebページ(HTMLドキュメント)から取得する場合は、UIWebViewDidfinishLoading デリゲートメソッド内でこのコードを使用します。

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

<br>は何にも置き換えられていません...これは望ましくありません。
Nishant 2013

2

<>を解析するのが最も安全な方法だと思いますが、違いますか?文字列全体をループし、<>で囲まれていないものを新しい文字列にコピーします。


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
}

男は、stringByReplacingOccurrencesOfStringUは、外部使用サイクルは、パーセントエンコーディングであり、適切な方法を介して固定されるべきです。
Vyachaslav Gerchicov

0

Three20フレームワークを使用する場合は、stringByRemovingHTMLTagsメソッドを追加するNSStringのカテゴリがあります。Three20CoreサブプロジェクトのNSStringAdditions.hを参照してください。


26
念のために、Three20を何にも使用しないでください。これまでで最も肥大化し、悪いコメントのフレームワーク。
kompozer

0

これをm.kocikowskiとDan Jの回答からさらに拡張し、初心者向けの説明を追加します

1#最初に、どのクラスでもコードを使用できるようにするために、objective-c-categoriesを作成する必要があります。

.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によって作成されたすべての一時的な文字列をクリーンアップするためにautoreleasepoolを利用するように少し変更しました

このメソッドのコメントには、/ *範囲内の文字を指定された文字列で置き換え、新しい文字列を返します。* /

したがって、XMLの長さに応じて、次の@autoreleasepoolの終わりまでクリーンアップされない新しい自動解放文字列の巨大な山を作成している可能性があります。それがいつ発生するかわからない場合、またはユーザーアクションがこのメソッドへの多くの呼び出しを繰り返しトリガーする可能性がある場合は、これを@autoreleasepoolにラップするだけです。これらはネストして、可能な場合はループ内で使用することもできます。

@autoreleasepoolに関するAppleのリファレンスはこれを述べています...「多くの一時オブジェクトを作成するループを作成する場合。ループ内で自動解放プールブロックを使用して、次の反復の前にそれらのオブジェクトを破棄できます。自動解放プールブロックをループで使用するアプリケーションの最大メモリフットプリントを削減するのに役立ちます。」私はそれをループで使用していませんが、少なくともこのメソッドはそれ自体でクリーンアップします。

- (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

別の1つの方法:

インターフェース:

-(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文字列を解析したいと思います。何をすべきか
Krutarth Patel

私のtime.niceソリューションを保存
Krutarth Patel

0

最近のiOSバージョンで機能する@ m.kocikowskiの更新された回答。

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

}


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.