"Заблокирован ресурс (файл заблокирован)" ошибка в Haskell

Я очень новичок в Haskell. Фактически, я работаю над в этом разделе этого урока. Я наткнулся на этот фрагмент кода:

import System.IO     
import Data.Char  

main = do     
    contents <- readFile "girlfriend.txt"     
    writeFile "girlfriendcaps.txt" (map toUpper contents) 

Что читает содержимое файла под названием "girlfriend.txt" и записывает версию файла с верхним обложением в новый файл под названием "girlfriendcaps.txt".

Итак, я хотел немного изменить код, чтобы принять имя файла. Я изменил код на это:

import System.IO
import Data.Char

main = do
    path <- getLine
    contents <- readFile path
    writeFile path (map toUpper contents)

теперь, очевидно, основное различие здесь в том, что я читаю и записываю в тот же файл. Поскольку я сейчас думаю об этом, это должно быть ленивая оценка, но я получаю сообщение об ошибке "resource busy". Исправьте меня, если я ошибаюсь, но я думаю, что readFile не начинает читать файл, пока writeFile не попросит его содержимое. А затем writeFile пытается записать в файл, но он все равно должен открыть файл, потому что он также запрашивает содержимое. Я рядом?

Итак, реальный вопрос: как мне читать и писать в тот же файл в Haskell? Имеет смысл, что это сложнее, потому что вы будете писать в другой файл из файла, который вы читаете чаще всего, но для моего собственного назидания, как бы вы читали и записывали в тот же файл?

Ответ 1

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

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

Ответ 2

В самом деле, это "ленивая оценка".

import System.IO
import Data.Char

main = do
    path <- getLine
    contents <- readFile path
    writeFile path (map toUpper contents)

Помните, что Haskell в первую очередь ленив в оценке, и поэтому большая часть подсистемы ввода-вывода. Поэтому, когда вы вызываете "readFile", вы начинаете передавать данные из файла. Когда вы сразу вызываете "writeFile", вы начинаете передавать байты обратно в один и тот же файл

Это будет ошибка (т.е. уничтожить ваши данные), поэтому Haskell блокирует ресурс, пока он не будет полностью оценен, и вы получите хорошее сообщение об ошибке.

Существует два решения:

  • Не разрушайте файл, а не копируйте его в новый файл
  • Или используйте строгий IO

Чтобы использовать строгий IO, рекомендуется использовать "текстовые" или "строгие" пакеты.