2011-07-27 10 views
1

この質問は、先にStack Overflowで質問されたRemove html character entities in a stringの質問に似ています。 しかし、受け入れられた答えは、名前付きHTMLエンティティの問題には対処していません。文字äの場合はäしたがって、すべてのHTMLをエスケープすることはできません。HTMLエンティティ(名前付きオブジェクトを含む)のアンエスケープ

非ASCII文字に名前付きHTMLエンティティを使用する従来のHTMLがあります。つまり、öの代わりにöäの代わりにäなどとなります。 A full list of all named HTML entitiesはWikipediaで入手できます。

私は、これらのHTMLエンティティを同等のキャラクタに迅速かつ効率的にエスケープしたいと考えています。


私は正規表現を使用して、Pythonの3でこれを行うには、コードを持っている:

import re 
import html.entities 

s = re.sub(r'&(\w+?);', lambda m: chr(html.entities.name2codepoint[m.group(1)]), s) 

正規表現は、しかし、非常に、人気のある高速またはHaskellで使いやすいようではありません。


Text.HTML.TagSoup.Entity(tagsoup)はコードポイントTPOマッピングという名前のエンティティのための便利なテーブルと機能を備えています。この、および正規表現-TDFAパッケージを使用して、私はHaskellでは同等極端に遅いを作り上げてきました:

{-# LANGUAGE OverloadedStrings #-} 
import Data.ByteString.Lazy.Char8 as L 
import Data.ByteString.Lazy.UTF8 as UTF8 
import Text.HTML.TagSoup.Entity (lookupEntity) 
import Text.Regex.TDFA ((=~~)) 

unescapeEntites :: L.ByteString -> L.ByteString 
unescapeEntites = regexReplaceBy "&#?[[:alnum:]]+;" $ lookupMatch 
where 
    lookupMatch m = 
    case lookupEntity (L.unpack . L.tail . L.init $ m) of 
     Nothing -> m 
     Just x -> UTF8.fromString [x] 

-- regex replace taken from http://mutelight.org/articles/generating-a-permalink-slug-in-haskell 
regexReplaceBy :: L.ByteString -> (L.ByteString -> L.ByteString) -> L.ByteString -> L.ByteString 
regexReplaceBy regex f text = go text [] 
where 
    go str res = 
    if L.null str 
     then L.concat . reverse $ res 
     else 
     case (str =~~ regex) :: Maybe (L.ByteString, L.ByteString, L.ByteString) of 
      Nothing -> L.concat . reverse $ (str : res) 
      Just (bef, match , aft) -> go aft (f match : bef : res) 

unescapeEntities機能は、上記のPythonのバージョンよりも遅く数桁を実行します。 Pythonコードは7秒で約130 MBを変換できますが、私のHaskellバージョンは数分間実行されます。

私はスピードの点で、より良い解決策を探しています。しかし、可能であれば正規表現を避けたいと思います(とにかく正規表現を避けることは、ハスケルと手を携えているようです)。

+0

あなたの実際の質問はここにはっきりしていません。より良いソリューションをお探しですか?現在のものを改善してもらいたいですか? –

+1

ご質問が不明な場合は申し訳ありません。はい、私はより良い解決策を望んでいます。私は1です。遅すぎる2.正規表現を使用していますが、それは非常にハスケル慣習的ではありません。私の解決策は、主に「これまでに得たことです」という出発点として与えられました。私は根本的なアイデアにはオープンです。 – vicvicvic

+0

どうやってファイルを読んでいるのですか?私が 'main = Data.ByteString.interact unescapeEntites'を作成し、' time cat big.txt | ./regex >>/dev/null'私は143Mが大きいために30秒を得る。txt(これはTagSoupにリストされているすべてのエンティティで、多くの 'a'が散在しています)。この間接的なことから、まだまだ不器用ですが、分はありません。 – applicative

答えて

1

私のバージョンです。 ByteStringの代わりにStringを使用します。

import Text.HTML.TagSoup.Entity (lookupEntity) 

unescapeEntities :: String -> String 
unescapeEntities [] = [] 
unescapeEntities ('&':xs) = 
    let (b, a) = break (== ';') xs in 
    case (lookupEntity b, a) of 
    (Just c, ';':as) -> c : unescapeEntities as  
    _    -> '&' : unescapeEntities xs 
unescapeEntities (x:xs) = x : unescapeEntities xs 

高価な正規表現演算を使用しないため、高速です。私はそれをテストしていない。あなたはそれがより速く必要な場合は、ByteStringまたはData.Textに適合させることができます。

0

web-encodingsパッケージをインストールし、decodeHtml関数のSourcecodeを取得し、必要な文字を追加することができます(私にとってはうまくいく)。これで必要なのは次のとおりです。

import Data.Maybe 
import qualified Web.Encodings.StringLike as SL 
import Web.Encodings.StringLike (StringLike) 
import Data.Char (ord) 

-- | Decode HTML-encoded content into plain content. 
-- 
-- Note: this does not support all HTML entities available. It also swallows 
-- all failures. 
decodeHtml :: StringLike s => s -> s 
decodeHtml s = case SL.uncons s of 
    Nothing -> SL.empty 
    Just ('&', xs) -> fromMaybe ('&' `SL.cons` decodeHtml xs) $ do 
     (before, after) <- SL.breakCharMaybe ';' xs 
     c <- case SL.unpack before of -- this are small enough that unpack is ok 
      "lt" -> return '<' 
      "gt" -> return '>' 
      "amp" -> return '&' 
      "quot" -> return '"' 
      '#' : 'x' : hex -> readHexChar hex 
      '#' : 'X' : hex -> readHexChar hex 
      '#' : dec -> readDecChar dec 
      _ -> Nothing -- just to shut up a warning 
     return $ c `SL.cons` decodeHtml after 
    Just (x, xs) -> x `SL.cons` decodeHtml xs 

readHexChar :: String -> Maybe Char 
readHexChar s = helper 0 s where 
    helper i "" = return $ toEnum i 
    helper i (c:cs) = do 
     c' <- hexVal c 
     helper (i * 16 + c') cs 

hexVal :: Char -> Maybe Int 
hexVal c 
    | '0' <= c && c <= '9' = Just $ ord c - ord '0' 
    | 'A' <= c && c <= 'F' = Just $ ord c - ord 'A' + 10 
    | 'a' <= c && c <= 'f' = Just $ ord c - ord 'a' + 10 
    | otherwise = Nothing 

readDecChar :: String -> Maybe Char 
readDecChar s = do 
    case reads s of 
     (i, _):_ -> Just $ toEnum (i :: Int) 
     _ -> Nothing 

私はパフォーマンスをテストしませんでした。しかし、正規表現を使わずにこれを行うことができれば、素晴らしいサンプルになるかもしれません。

関連する問題