3

我正在尝试读取脚本文件,然后处理并将其输出到 html 文件。在我的脚本文件中,只要有@title(this is a title) ,我就会在我的 html 输出中添加标签[header] 这是一个标题 [/header] 。所以我的做法是先读取脚本文件,将内容写入字符串,处理字符串,再将字符串写入html文件。

为了识别@title,我需要逐个字符地读取字符串中的字符。当我阅读“@”时,我需要检测下一个字符以查看它们是否是标题 e。

问题:如何在 Haskell 中遍历一个字符串(这是一个字符列表)?

4

3 回答 3

4

例如,您可以使用简单的递归技巧

findTag [] = -- end of list code.
findTag ('@':xs)
  | take 5 xs == "title" = -- your code for @title
  | otherwise            = findTag xs
findTag (_:xs) = findTag xs

所以基本上你只是模式匹配,如果下一个字符(列表头)是'@',然后你检查接下来的 5 个字符是否形成“标题”。如果是这样,您可以继续解析代码。如果下一个字符不是'@',你只需继续递归。一旦列表为空,您将到达第一个模式匹配。

其他人可能有更好的解决方案。

我希望这回答了你的问题。

编辑:

为了获得更多的灵活性,如果你想找到一个特定的标签,你可以这样做:

findTag [] _ = -- end of list code.
findTag ('@':xs) tagName
  | take (length tagName) xs == tagName = -- your code for @title
  | otherwise = findTag xs
findTag (_:xs) _ = findTag xs

这样,如果你这样做

findTag text "title"

您将专门寻找标题,并且您可以随时将标记名更改为您想要的任何内容。

另一个编辑:

findTag [] _ = -- end of list code.
findTag ('@':xs) tagName
  | take tLength xs == tagName = getTagContents tLength xs
  | otherwise = findTag xs
  where tLength = length tagName
findTag (_:xs) _ = findTag xs

getTagContents :: Int -> String -> String
getTagContents len = takeWhile (/=')') . drop (len + 1) 

老实说,它变得有点混乱,但这就是正在发生的事情:

您首先删除 tagName 的长度,然后再删除一个用于左括号的长度,然后使用 takeWhile 将字符提取到右括号。

于 2013-02-16T18:17:05.527 回答
3

显然您的问题属于解析类别。正如 Daniel Wagner 明智地指出的那样,出于可维护性的原因,您最好使用解析器来处理它。

另一件事是,如果您想有效地处理文本数据,最好使用Text而不是String.

以下是使用Attoparsec解析器库解决问题的方法:

-- For autocasting of hardcoded strings to `Text` type
{-# LANGUAGE OverloadedStrings #-}

-- Import a way more convenient prelude, excluding symbols conflicting 
-- with the parser library. See
-- http://hackage.haskell.org/package/classy-prelude
import ClassyPrelude hiding (takeWhile, try)
-- Exclude the standard Prelude
import Prelude ()
import Data.Attoparsec.Text

-- A parser and an inplace converter for title
title = do
  string "@title("
  r <- takeWhile $ notInClass ")"
  string ")"
  return $ "[header]" ++ r ++ "[/header]"

-- A parser which parses the whole document to parts which are either
-- single-character `Text`s or modified titles
parts = 
  (try endOfInput >> return []) ++
    ((:) <$> (try title ++ (singleton <$> anyChar)) <*> parts)

-- The topmost parser which concats all parts into a single text
top = concat <$> parts

-- A sample input
input = "aldsfj@title(this is a title)sdlfkj@title(this is a title2)"

-- Run the parser and output result
main = print $ parseOnly top input

这输出

Right "aldsfj[header]this is a title[/header]sdlfkj[header]this is a title2[/header]"

PS ClassyPrelude 重新实现++Monoid's的别名mappend,因此您可以根据需要将其替换为mappend,<>Alternative's <|>

于 2013-02-16T22:19:31.290 回答
0

对于模式搜索和替换,您可以使用 streamEdit.

import Replace.Megaparsec
import Text.Megaparsec
import Text.Megaparsec.Char

title :: Parsec Void String String
title = do
    void $ string "@title("
    someTill anySingle $ string ")"

editor t = "[header]" ++ t ++ "[/header]"

streamEdit title editor " @title(this is a title) "
" [header]this is a title[/header] "
于 2019-08-31T11:29:46.590 回答