2016-08-15 3 views
0

УстановкиСинтаксические первое вхождение слова, не precded белого пространства

Мне нужно найти первое вхождение слова в некотором текстовом файле, который не предшествует белым пространством. Вот возможные случаи:

-- * should succed 
t1 = "hello\t999\nworld\t\900" 
t2 = "world\t\900\nhello\t999\n" 
t3 = "world world\t\900\nhello\t999\n" 

-- * should fail 
t4 = "world\t\900\nhello world\t999\n" 
t5 = "hello world\t999\nworld\t\900" 
t6 = "world hello\t999\nworld\t\900" 

Сейчас t6 преуспевает, хотя он должен потерпеть неудачу, потому что мой анализатор будет потреблять любой символ, пока он не достигнет привет. Вот мой парсер:

мое решение

import Control.Applicative 

import Data.Attoparsec.Text.Lazy 
import Data.Attoparsec.Combinator 
import Data.Text hiding (foldr) 
import qualified Data.Text.Lazy as L (Text, pack) 



-- * should succed 
t1 = L.pack "hello\t999\nworld\t\900" 
t2 = L.pack "world\t\900\nhello\t999\n" 

-- * should fail 
t3 = L.pack "world\t\900\nhello world\t999\n" 
t4 = L.pack "hello world\t999\nworld\t\900" 
t5 = L.pack "world hello\t999\nworld\t\900" 

p = occur "hello"  

---- * discard all text until word `w` occurs, and find its only field `n` 
occur :: String -> Parser (String, Int) 
occur w = do 
    pUntil w 
    string . pack $ w 
    string "\t" 
    n <- natural 
    string "\n" 
    return (w, read n) 


-- * Parse a natural number 
natural :: Parser String 
natural = many1' digit 

-- * skip over all words in Text stream until the word we want 
pUntil :: String -> Parser String 
pUntil = manyTill anyChar . lookAhead . string . pack 
+0

Парсер * не * правильный инструмент для «найти первое вхождение х в последовательности у». Вы должны разобрать всю строку в структуру данных, в которой хранятся пары (ключ, значение) вместе с положением, в котором они произошли. Ваша текущая проблема - 't6' содержит две пары ключ/значение (одна во всей строке, одна в суффиксе), так что, естественно, парсер обратной связи находит оба. Разбор каждого ключа безусловно фиксирует это. С attoparsec вы ограничены получением позиции в виде байтового индекса, но этого должно быть достаточно для ваших целей. – user2407038

ответ

2

Вот подход рассмотреть следующие вопросы:

{-# LANGUAGE OverloadedStrings #-} 

import Control.Applicative 

import Data.Attoparsec.Text.Lazy 
import Data.Attoparsec.Combinator 
import Data.Text hiding (foldr) 
import qualified Data.Text.Lazy as L (Text, pack) 
import Data.Monoid 

natural = many1' digit 

-- manyTill anyChar (try $ char c <* eof) 

pair0 w = do 
    string (w <> "\t") 
    n <- natural 
    string "\n" 
    return n 

pair1 w = do 
    manyTill anyChar (try $ string ("\n" <> w <> "\t")) 
    n <- natural 
    string "\n" 
    return n 

pair w = pair0 w <|> pair1 w 

t1 = "hello\t999\nworld\t\900" 
t2 = "world\t\900\nhello\t999\n" 
t3 = "world world\t\900\nhello\t999\n" 

-- * should fail 
t4 = "world\t\900\nhello world\t999\n" 
t5 = "hello world\t999\nworld\t\900" 
t6 = "world hello\t999\nworld\t\900" 

test t = parseTest (pair "hello") (L.pack t) 

main = do 
    test t1; test t2; test t3 
    test t4; test t5; test t6 

Идея заключается в том, что pair0 соответствует паре с данным ключом в начале ввод и pair1 соответствует паре сразу после новой строки.

Ключом является использование manyTill anyChar (try p), который будет пропускать символы , пока анализатор p не удастся.

(кстати - я узнал об этом использовании manyTill и try, прочитав ответ, написанный @Cactus.)

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

  • Нет связанных вопросов^_^