2013-02-16 2 views
2

스크립트 파일을 읽고 나서 html 파일로 출력하려고합니다. 내 스크립트 파일에 @ 제목 (제목입니다)이있을 때마다 태그 [헤더]이 (가) 내 html 출력에 [제목]입니다. 그래서 내 접근 방식은 먼저 스크립트 파일을 읽고 문자열에 내용을 쓰고 문자열을 처리 한 다음 html 파일에 문자열을 쓰는 것입니다.하스켈 : 문자열/텍스트 파일을 통과 함

@title을 인식하는 데는 문자로 된 문자를 읽어야합니다. '@'를 읽을 때 다음 문자를 감지하여 해당 문자가 아닌지 확인해야합니다.

질문 : 하스켈에서 문자열 (char 목록)을 어떻게 트래버스합니까?

+4

파서를 작성하십시오. 단기간에 다른 해킹을 더 쉽게 할 수는 있지만 나중에 후회할 것입니다. –

+3

그리고 파서의 주제에 관해서는 파섹이 소유하고 있습니다. –

+0

@CatPlusPlus 논쟁의 여지가 있습니다. 성능면에서 Attoparsec은 종종 그것을 앞 당깁니다. –

답변

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) 

는 솔직히 말해서, 그것은 조금 지저분한 받고 있지만, 여기에 무슨 일이 일어나고 있는지의 것 :

당신은 먼저 태그 이름의 길이를 드롭, 다음 중 하나 이상을 오픈 브래킷 및 그런 다음 takeWhile을 사용하여 닫기 대괄호까지 문자를 가져 와서 마무리합니다.

+0

제안 해 주셔서 감사합니다. –

3

문제는 파싱 범주에 해당하는 것 같습니다. Daniel Wagner가 현명하게 말했듯이, 유지 보수성을 이유로 파서 (parser)를 사용하여 접근하는 것이 훨씬 낫습니다.

텍스트 데이터를 효율적으로 사용하려면 String 대신 Text을 사용하는 것이 좋습니다.

-- 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 = "[email protected](this is a title)[email protected](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]" 

P.S. 출력 : 여기에

당신이 Attoparsec 파서 라이브러리를 사용하여 문제를 해결할 수있는 방법 ClassyPrelude는 Monoidmappend의 별칭으로 ++을 다시 구현하므로 원하는 경우 mappend, <> 또는 Alternative<|>으로 바꿀 수 있습니다.