2012-01-30 20 views
17

Esiste un'API di alto livello per eseguire ricerche e sostituzioni con espressioni regex in Haskell? In particolare, sto osservando i pacchetti Text.Regex.TDFA o Text.Regex.Posix. Mi piacerebbe davvero come qualcosa di tipo:sostituzione/sottostringa con le librerie regex di Haskell

f :: Regex -> (ResultInfo -> m String) -> String -> m String 

così, ad esempio, di sostituire "cane" con "gatto" si potrebbe scrivere

runIdentity . f "dog" (return . const "cat") -- :: String -> String 

o fare cose più avanzate con la monade, come conteggio di occorrenze, ecc.

La documentazione di Haskell per questo è piuttosto carente. Alcune note API di basso livello sono here.

risposta

4

Non so di qualsiasi funzione esistente che crea questa funzionalità, ma penso che sarei andato a finire con qualcosa come il AllMatches [] (MatchOffset, MatchLength) instance of RegexContent simulare:

replaceAll :: RegexLike r String => r -> (String -> String) -> String -> String 
replaceAll re f s = start end 
    where (_, end, start) = foldl' go (0, s, id) $ getAllMatches $ match re s 
     go (ind,read,write) (off,len) = 
      let (skip, start) = splitAt (off - ind) read 
       (matched, remaining) = splitAt len matched 
      in (off + len, remaining, write . (skip++) . (f matched ++)) 

replaceAllM :: (Monad m, RegexLike r String) => r -> (String -> m String) -> String -> m String 
replaceAllM re f s = do 
    let go (ind,read,write) (off,len) = do 
     let (skip, start) = splitAt (off - ind) read 
     let (matched, remaining) = splitAt len matched 
     replacement <- f matched 
     return (off + len, remaining, write . (skip++) . (replacement++)) 
    (_, end, start) <- foldM go (0, s, return) $ getAllMatches $ match re s 
    start end 
28

Come circa il subRegex nella confezione di testo .Regex?

Prelude Text.Regex> :t subRegex 
subRegex :: Regex -> String -> String -> String 

Prelude Text.Regex> subRegex (mkRegex "foo") "foobar" "123" 
"123bar" 
1

forse questo approccio si adatta.

import Data.Array (elems) 
import Text.Regex.TDFA ((=~), MatchArray) 

replaceAll :: String -> String -> String -> String   
replaceAll regex new_str str = 
    let parts = concat $ map elems $ (str =~ regex :: [MatchArray]) 
    in foldl (replace' new_str) str (reverse parts) 

    where 
    replace' :: [a] -> [a] -> (Int, Int) -> [a] 
    replace' new list (shift, l) = 
     let (pre, post) = splitAt shift list 
     in pre ++ new ++ (drop l post) 
3

in base alla risposta @ di raperonzolo, ma con l'errore di battitura fisso in modo che non solo <<loop>>:

replaceAll :: Regex -> (String -> String) -> String -> String 
replaceAll re f s = start end 
    where (_, end, start) = foldl' go (0, s, id) $ getAllMatches $ match re s 
     go (ind,read,write) (off,len) = 
      let (skip, start) = splitAt (off - ind) read 
       (matched, remaining) = splitAt len start 
      in (off + len, remaining, write . (skip++) . (f matched ++)) 
1

È possibile utilizzare replaceAll dal Data.Text.ICU.Replace module.

Prelude> :set -XOverloadedStrings 
Prelude> import Data.Text.ICU.Replace 
Prelude Data.Text.ICU.Replace> replaceAll "cat" "dog" "Bailey is a cat, and Max is a cat too." 
"Bailey is a dog, and Max is a dog too." 
Problemi correlati