2013-02-16 5 views
2

Я пытаюсь прочитать файл сценария, а затем обработать его и вывести его в html-файл. В моем файле сценария, когда есть @title (это название), я добавлю тег [header] это заголовок [/ header] в моем выходе html. Поэтому мой подход - сначала прочитать файл сценария, записать содержимое в строку, обработать строку, а затем записать строку в файл html.Haskell: Прохождение через строковый/текстовый файл

В другом, чтобы распознать @title, мне нужно будет прочитать символ по символу в строке. Когда я прочитаю «@», мне нужно будет обнаружить следующий символ, чтобы посмотреть, не являются ли они.

ВОПРОС: Как пройти через строку (которая является списком символов) в Haskell?

+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 символов образуют "заглавие". если это так, вы можете продолжить свой синтаксический код. если следующий символ isnt '@', вы просто продолжаете рекурсию. Как только список пуст, вы достигнете первого совпадения.

У кого-то может быть лучшее решение.

Надеюсь, это ответит на ваш вопрос.

редактировать:

Для гибкости немного больше, если вы хотите найти конкретный тег вы могли бы сделать это:

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

Очевидно, ваша проблема попадает в категорию разбора. Как мудро заявил Дэниел Вагнер, для удобства обслуживания вы гораздо лучше приближаетесь к нему, как правило, с парсером.

Другое дело, если вы хотите эффективно работать с текстовыми данными, вам лучше использовать 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 = "[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. ClassyPrelude reimplements ++ в качестве псевдонима для Monoidmappend, поэтому вы можете заменить его mappend, <> или Alternative<|>, если хотите.

Смежные вопросы