我想执行字符串搜索并使用正则表达式替换,但无法找到满足我需要的功能。我正在寻找类似于我的神话regexReplace
的东西,我的潜在签名如下:
regexReplace :: Regex -> (String -> String) -> String -> String
或
regexReplace :: Regex -> (RegexMatch -> String) -> String -> String
这大致相当于PHP中的preg_replace_callback
。它采用由Regex
表示的编译正则表达式,替换函数(其参数可以是String
或某些匹配类型RegexMatch
),源字符串并返回结果。
在那里的许多Haskell正则表达式库中,与此行为最接近的匹配是什么?
更新
我可能需要在IO
内运行回调,因此我可能最终需要以下函数签名:
regexReplace :: Monad m => Regex -> (RegexMatch -> m String) -> String -> m String
这显然是一个比原始问题陈述更普遍的问题。我最终可能会使用Parsec或类似的东西。
基本上,我有以下程序,但无法弄清楚如何填空:
{-# LANGUAGE RecordWildCards #-}
module Main where
import Data.Maybe
import System.Environment
data Regex = Regex {
regexPattern :: String
}
data RegexMatch = RegexMatch {
regexGroups :: [String]
}
regexReplace :: Monad m => Regex -> (RegexMatch -> m String) -> String -> m String
regexReplace Regex{..} f source =
-- What do I put here?
undefined
replacementValue :: [String] -> IO String
replacementValue groups =
case groups of
whole : name : defaultValue : [] -> do
lookupResult <- lookupEnv name
let value = fromMaybe defaultValue lookupResult
return value
otherwise -> error "Unexpected groups"
main :: IO ()
main = do
replaceResult <- regexReplace
(Regex "ENV:([A-Za-z_][A-Za-z_0-9]*):([A-Za-z_][A-Za-z_0-9]*)")
(\RegexMatch{..} -> replacementValue regexGroups)
"some input containing ENV:foo:bar replacement expressions"
putStrLn replaceResult
结论:
根据@DanielWagner的建议使用regex-applicative
,我想出了下面的解决方案非常优雅:
{-# LANGUAGE RecordWildCards #-}
module Main (main) where
import Data.Char
import Data.Functor.Compose
import Data.Maybe
import System.Environment
import Text.Regex.Applicative
data EnvMatch = EnvMatch {
envMatchName :: String
, envMatchDefault :: String
}
-- Matches pattern "_env:SOME_NAME:SOME_DEFAULT_VALUE"
envPattern :: RE Char EnvMatch
envPattern = EnvMatch
<$ string "_env:"
<*> token
<* string ":"
<*> token
where
token :: RE Char String
token = many (psym $ \c -> isAlphaNum c || c == '_')
expandEnv :: EnvMatch -> IO String
expandEnv EnvMatch{..} = fmap (fromMaybe envMatchDefault) (lookupEnv envMatchName)
queryTransform :: RE Char (IO String)
queryTransform = getCompose . (concat <$>) . sequenceA . map Compose $
[
pure <$> many anySym
, expandEnv <$> envPattern
, pure <$> many anySym
]
runQueryTransform :: String -> IO (Maybe String)
runQueryTransform = sequenceA . match queryTransform
main :: IO ()
main = do
result <- runQueryTransform "hello\"_env:HOME:default_home_dir\"world"
print result
-- Yields: Just "hello\"/home/user\"world"
谢谢,@丹尼尔瓦格纳!
答案 0 :(得分:6)
您可能希望regex-applicative提供:
match :: RE Char String -> String -> Maybe String
您可以在构建RE Char String
类型值的代码中替换匹配的特定部分。例如,这是一个函数,它找到a
和b
个字符的字符串并将它们反转:
import Text.Regex.Applicative
asAndBs = many (psym ( `elem` "ab"))
noAsAndBs = many (psym (`notElem` "ab"))
transformation = concat <$> sequenceA [noAsAndBs, reverse <$> asAndBs, noAsAndBs]
一些例子在ghci中运行:
> match transformation "ntoheuuaaababbboenuth"
Just "ntoheuubbbabaaaoenuth"
> match transformation "aoesnuthaosneut"
Nothing
要处理您更新的问题:此处是一个转换,它会查找一串a
和b
个字符,并询问用户要替换它们的内容。它重复使用之前的asAndBs
和noAsAndBs
,仅修改应用于它们的转换。我还提供了queryTransform
的示例驱动程序,仅用于说明如何使用它。基本思想是建立一个生成替换字符串的IO
动作而不是平坦的替换字符串。然后,调用match
的消费者可以根据需要执行IO
操作。
import Data.Functor.Compose
queryTransform = getCompose . (concat <$>) . sequenceA . map Compose $
[ pure <$> noAsAndBs
, getLine <$ asAndBs
, pure <$> noAsAndBs
]
runQueryTransform = getLine >>= sequenceA . match queryTransform
我希望你能认识到之前queryTransform
结构和transformation
结构之间的相似之处(特别注意(concat <$>) . sequenceA
结构就像之前一样)。这是ghci中的一些例子:
> runQueryTransform
oeunthaaabbbaboenuth
replacement
Just "oeunthreplacementoenuth"
> runQueryTransform
aoeunthaoeunth
Nothing
答案 1 :(得分:0)
您有chrome.tabs.onActivated,其中包含abort
。我不确定PHP中的回调是什么,但我认为你不能直接将其转换为Haskell。
答案 2 :(得分:0)
如果要在Haskell中执行搜索和替换,请考虑
模式匹配
megaparsec
解析器而不是正则表达式。接着就,随即
您可以使用
streamEdit
,
确切地具有您神话般的regexReplace
函数的签名,或者
streamEditT
,允许在IO
内部运行回调。
这是您优雅的regex-applicative
解决方案的解析器变体,
需要queryTransform
或runQueryTransform
函数。
:set -XRecordWildCards
:set -XTypeFamilies
import Replace.Megaparsec
import Text.Megaparsec
import Text.Megaparsec.Char
import System.Environment
import Data.Char
import Data.Maybe
data EnvMatch = EnvMatch
{ envMatchName :: String
, envMatchDefault :: String
}
-- Matches pattern "_env:SOME_NAME:SOME_DEFAULT_VALUE"
envPattern = EnvMatch
<$ string "_env:"
<*> tokes
<* string ":"
<*> tokes
where
tokes = many $ satisfy (\c -> isAlphaNum c || c == '_')
expandEnv :: EnvMatch -> IO String
expandEnv EnvMatch{..} = fmap (fromMaybe envMatchDefault) (lookupEnv envMatchName)
streamEditT envPattern expandEnv "hello\"_env:HOME:default_home_dir\"world"
"hello\"/home/user\"world"