I'm very new to Haskell. In fact, I'm working through this section of this tutorial. I came across this piece of code:
import System.IO
import Data.Char
main = do
contents <- readFile "girlfriend.txt"
writeFile "girlfriendcaps.txt" (map toUpper contents)
Which reads the contents of the file called "girlfriend.txt" and writes the upper-cased version of the file to a new file called "girlfriendcaps.txt".
So, I wanted to modify the code a bit to take the name of the file to act on. I changed the code to this:
import System.IO
import Data.Char
main = do
path <- getLine
contents <- readFile path
writeFile path (map toUpper contents)
now, obviously the major difference here is that I'm reading from and writing to the same file. As I'm thinking about it now, this must be a lazy-evaluation thing, but i'm getting the "resource busy" error message. Correct me if I'm wrong, but I guess that readFile doesn't start reading the file until writeFile asks for the contents of it. And then writeFile tries to write to the file, but it must still have the file open because it's also asking for the contents. Am I close there?
So, the real question is: how do I read from and write to the same file in Haskell? It makes sense that it's more difficult, because you will write to a different file from the file you read from more often than not, but for my own edification, how would you read and write to the same file?
Indeed, this is a "lazy evaluation thing".
import System.IO
import Data.Char
main = do
path <- getLine
contents <- readFile path
writeFile path (map toUpper contents)
Remember that Haskell is primarily lazy in evaluation, and so is much of the IO subsystem. So when you call 'readFile' you begin streaming data in from the file. When you then immediately call "writeFile" you start streaming bytes back to the same file
This would be an error (i.e. destroy your data), so Haskell locks the resource until it is fully evaluated, and you get a nice error message.
There are two solutions:
To use strict IO, the 'text' or 'strict' packages are recommended.
What you're looking for is how to open a file in ReadWriteMode
.
fileHandle <- openFile "fileName.txt" ReadWriteMode
contents <- hGetContents fileHandle
There's trickier stuff for navigating forwards and backwards through the file.
See Working with files and handles from RWH, and Operations on Handles at the System.IO docs.
Depends on exactly what you are trying to do. As a rule, in any language, this is probably a bad design because if anything goes wrong either inside the program or outside (e.g user error) then you have destroyed your original data and cannot try again. It also requires that the entire file be held in memory, which is cool if its just a few bytes, but not so good when someone decides to run this on a really big file.
If you really want to do this then generate a temporary filename for the output, and then once you know that you have written to it successfully you can delete the original and rename the new one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With