Прочитать файл как строку байтов и записать эту строку байтов в файл: проблема на сетевом диске

Рассмотрим следующую простую программу на Haskell, которая читает файл как строку байтов и записывает файл tmp.tmp из этой строки байтов:

module Main
  where
import System.Environment
import qualified Data.ByteString.Lazy as B

main :: IO ()
main = do
  [file] <- getArgs
  bs <- B.readFile file
  action <- B.writeFile "tmp.tmp" bs
  putStrLn "done"

Он скомпилирован в исполняемый файл с именем tmptmp.

У меня на компьютере два жестких диска: диск C и диск U, и этот диск является сетевым, и этот сетевой диск отключен.

Теперь попробуем tmptmp.

Когда я запускаю его из C, проблем нет; Я запускаю его два раза ниже, первый раз с файлом на C и второй раз с файлом на U:

C:\HaskellProjects\imagelength> tmptmp LICENSE
done

C:\HaskellProjects\imagelength> tmptmp U:\Data\ztemp\test.xlsx
done

Сейчас запускаю с U, с файлом на C диске, проблем нет:

U:\Data\ztemp> tmptmp C:\HaskellProjects\imagelength\LICENSE
done

Проблема возникает, когда я запускаю ее из U с файлом на U диске:

U:\Data\ztemp> tmptmp test.xlsx
tmptmp: tmp.tmp: openBinaryFile: resource busy (file is locked)

Если в моей программе я использую строгие строки байтов вместо ленивых строк байтов (путем замены Data.ByteString.Lazy на Data.ByteString), эта проблема больше не возникает.

Я хотел бы это понять. Любое объяснение? (Мне особенно хотелось бы знать, как решить эту проблему, но все еще используя ленивые строки байтов)

РЕДАКТИРОВАТЬ

Если быть точнее, проблема все еще возникает с этой программой:

import qualified Data.ByteString as SB
import qualified Data.ByteString.Lazy as LB

main :: IO ()
main = do
  [file] <- getArgs
  bs <- LB.readFile file
  action <- SB.writeFile "tmp.tmp" (LB.toStrict bs)
  putStrLn "done"

пока проблема исчезает с:

  bs <- SB.readFile file
  action <- LB.writeFile "tmp.tmp" (LB.fromStrict bs)

Похоже, проблема в лени readFile.


person Stéphane Laurent    schedule 17.06.2017    source источник
comment
1. Работает ли это, если вы укажете ему абсолютный путь (т. воспроизвести, но я не уверен, как получить доступ к сетевому диску, который находится в автономном режиме (очевидно, я неправильно понимаю значение слова «автономный» здесь!). 3. Зачем вам нужно использовать ленивый BS? Кажется, вы обнаружили, что Strict правильный инструмент для работы 4. Работает ли это, если вы принудительно вводите (т.е. evaluate (length bs) перед записью)?   -  person user2407038    schedule 17.06.2017
comment
Привет @ user2407038. 1) Нет. 2) Это мой рабочий ноутбук, и я не подключен к домену. В Проводнике Windows у вас есть кнопка Работать автономно/Работать онлайн. Нажмите «Работать в автономном режиме», если хотите воспроизвести. 3) Это всего лишь минимальный воспроизводимый пример. В реальной жизни я использую библиотеку xlsx, которая работает с ленивыми строками байтов. 4) Функцию evaluate не знал, попробую.   -  person Stéphane Laurent    schedule 17.06.2017
comment
2) Или просто отключите свой компьютер от Интернета.   -  person Stéphane Laurent    schedule 17.06.2017
comment
Я только что решил свою реальную проблему, используя стратегию последней точки моего редактирования, с LB.readFile, затем fromStrict. Но очевидно, что это не дает объяснения.   -  person Stéphane Laurent    schedule 17.06.2017
comment
К сожалению, воспроизвести не могу (на W7). Я думаю, это потому, что у меня нет фактического удаленного местоположения, к которому я мог бы получить доступ таким образом, но Windows позволила мне сопоставить сетевой диск с локальной (общей) папкой. С этой настройкой нет кнопки «Работать в автономном режиме», и она отлично работала с ленивой ByteString.   -  person user2407038    schedule 18.06.2017
comment
Спасибо за попытку воспроизвести @user2407038. Я тоже использую W7. Я еще что-то не сказал и тогда это могло быть причиной, но это было бы странно. Похоже, этот сетевой диск неправильно сконфигурирован. Например, согласно функции file.access R, диск U недоступен для записи (пока он доступен для записи). Но было бы странно, что U распознается как недоступное для записи только тогда, когда я использую ленивую строку байтов.   -  person Stéphane Laurent    schedule 18.06.2017
comment
То же самое с getPermissions из System.Directory: getPermissions "U:/Data" указывает, что U:/Data недоступно для записи... хм, но, тем не менее, указывает, что U:/Data/zTemp доступно для записи.   -  person Stéphane Laurent    schedule 18.06.2017


Ответы (1)


Согласно последним данным Документы .ByteString.Lazy:

Использование ленивых функций ввода-вывода, таких как readFile или hGetContents, означает, что порядок операций, таких как закрытие дескриптора файла, остается на усмотрение RTS.

Пример, приведенный с отключенным сетевым диском, по-видимому, приводит к тому, что RTS продолжает работу с readFile, не закрывая файл. Документы, которые имеют почти идентичный пример, говорят, что

Когда затем выполняется writeFile, [tmp.tmp] все еще открыт для чтения, и RTS старается не открывать его одновременно для записи, вместо этого возвращая ошибку.

Насколько мне известно, решения для этого в Data.ByteString.Lazy нет — в документации предлагается как ваше решение (использующее строгое чтение), так и другие пакеты. Иногда чтение и запись одного и того же файла могут работать, но у вас нет гарантии.

person Manfred Paul    schedule 15.05.2021