Чтение из файла, содержащего точки

У меня есть файл txt, который содержит два числа, например:

2.

3.

Как видите, каждый конец строки заканчивается точкой.

Как я могу прочитать его и распечатать, например, сумму этих чисел?

Если мой файл не содержит точек, например, у меня есть

2

3

это не проблема. Мой код:

main3 = do
    x <- openFile "C:/Users/file.txt"  ReadMode
    m <- hGetLine x
    n <- hGetLine x
    return ((read m::Int)+(read n::Int))

и это работает хорошо. Но когда в моем файле многоточие, я не знаю, что мне делать. Может есть какие библиотеки?

Спасибо за помощь.


person user2520031    schedule 25.06.2013    source источник


Ответы (3)


Проблема в том, что read не будет анализировать "3." как 3. Если вы просто хотите сделать это хакерским способом, вы можете удалить последний символ.

main4 = do
    x <- openFile "C:/Users/file.txt"  ReadMode
    m <- hGetLine x
    n <- hGetLine x
    return ((read (init m)::Int)+(read (init n)::Int))

который может работать, но довольно хрупок, поскольку предполагает, что единственный нецифровой символ в каждой строке является последним. Мы можем сделать немного лучше, предположив, что первые n цифры образуют наши числа.

import Data.Char

-- takeWhile isDigit :: String -> String

main5 path = do
  f <- readFile path
  numberStrings <- map (takeWhile isDigit) (lines f)
  sum (map read numberStrings)

Самым надежным решением было бы перейти на «библиотеку комбинаторов синтаксических анализаторов», такую ​​​​как Parsec, которая позволила бы вам записать грамматику вашего текстового файла.

person J. Abrahamson    schedule 25.06.2013
comment
Смотрите мой ответ для примера использования парсека - person Daniel Gratzer; 25.06.2013

Есть несколько способов сделать это. Самый простой из них

main = do
  text <- readFile "file.txt" -- Grab the file
  let nums = map read . map init . lines $ text
  print $ sum nums

init просто опускает .. Однако я бы написал так

import Text.Parsec.String
import Text.Parsec
import Control.Applicative ((<*), (<$>))

getNums :: Parser [Int]
getNums = num `sepEndBy` newline
    where num = read <$> many1 digit <* char '.'

main = parseFromFile getNums "filename" >>= print . fmap sum

Стоит ли использовать парсек для чего-то подобного? Ответ "это зависит". Мое эмпирическое правило заключается в том, что если я планирую использовать его более одного раза, то просто стисните зубы и используйте парсек. Гораздо проще изменить что-то, что использует библиотеку, например parsec, для новых и более сложных форматов. Кроме того, таким образом вы получаете бесплатные [достойные] сообщения об ошибках.

person Daniel Gratzer    schedule 25.06.2013
comment
Если вы стиснете зубы и используете uu-parsinglib, вы получите ОТЛИЧНЫЕ сообщения об ошибках! - person J. Abrahamson; 25.06.2013
comment
@tel Ну, они определенно лучше, чем Error prelude *** No parse или что-то в этом роде :) - person Daniel Gratzer; 25.06.2013

Вы можете сделать это в несколько шагов, используя монадический API Haskell:

  1. Читать в файле построчно
  2. Проверять, заканчивается ли точка точкой: использовать карту и возвращать монаду Maybe: Ничего, если строка не заканчивается точкой, Просто (значение без точки), когда строка заканчивается точкой
  3. Используйте map + flatten для преобразования каждой строки в число, опять же с помощью монады Maybe. Ничего, если есть ошибка, или Просто x, чтобы содержать число
  4. Суммируйте значения, используя fold

Когда у меня будет время, я попытаюсь построить пример. Всегда интересно поиграть с монадическим API Haskell.

person stefan.schwetschke    schedule 25.06.2013