How to close a stream properly?

自作多情 提交于 2020-01-04 10:59:08

问题


I have been assigned a task to write a program that will:

  1. Open a file.

  2. Read the content.

  3. Replace a specific word with another word.

  4. Save the changes to the file.

I know for sure that my code can open, read and replace words. The problem occurs when i add the "Save the changes to the file" - part. Here is the code:

open System.IO

//Getting the filename, needle and replace-word.
System.Console.WriteLine "What is the name of the file?"
let filename = string (System.Console.ReadLine ())

System.Console.WriteLine "What is your needle?"
let needle = string (System.Console.ReadLine ())

System.Console.WriteLine "What you want your needle replaced with?"
let replace = string (System.Console.ReadLine ())   

//Saves the content of the file
let mutable saveLine = ""   

//Opens a stream to read the file
let reader = File.OpenText filename

//Reads the file, and replaces the needle.
let printFile (reader : System.IO.StreamReader) =
  while not(reader.EndOfStream) do
    let line = reader.ReadLine ()
    let lineReplace = line.Replace(needle,replace)
    saveLine <- saveLine + lineReplace
    printfn "%s" lineReplace

//Opens a stream to write to the file
let readerWrite = File.CreateText(filename)

//Writes to the file
let editFile (readerWrite : System.IO.StreamWriter) =
  File.WriteAllText(filename,saveLine)

printf "%A" (printFile reader)

I get the error message "Sharing violation on path...", which makes me believe that the reading stream do not close properly. I have tried playing around with the structure of my code and tried different things for the .NET library, but i always get the same error message. Any help is much appreciated.


回答1:


Streams are normally closed by calling Stream.Close() or disposing them.

System.IO has methods to read or write complete files from/to arrays of lines. This would shorten the operation to something like this:

File.ReadAllLines filePath
|> Array.map (fun line -> line.Replace(needle, replace))
|> fun editedLines -> File.WriteAllLines(filePath, editedLines)

What documentation are you using? Have a look at the MSDN documentation for System.IO and the similar MSDN documentations for various things in .NET/the CLR; these answer questions like this one quickly.




回答2:


I retained most of your original code, although it's not very idiomatic. If you use use with disposable resources, .NET will clean up after you. See for example F# Docs and Fun&Profit, the latter also has a nice section on Expressions and syntax.

If you execute your code, you should get System.IO.IOException:

Unhandled Exception: System.IO.IOException: The process cannot access the file 'C:\Users\xcs\Documents\Visual Studio 2015\Projects\StackOverflow6\ConsoleApplication11\bin\Release\testout.txt' because it is being used by another process. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
at System.IO.StreamWriter.CreateFile(String path, Boolean append, Boolean checkHost) at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize, Boolean checkHost) at System.IO.StreamWriter..ctor(String path, Boolean append) at System.IO.File.CreateText(String path) at Program.op@46(Unit unitVar0) in C:\Users\xcs\Documents\Visual Studio 2015\Projects\StackOverflow6\ConsoleApplication11\Program.fs:line 74
at Program.main(String[] argv) in C:\Users\xcs\Documents\Visual Studio 2015\Projects\StackOverflow6\ConsoleApplication11\Program.fs:line 83

It starts at line 83, which is the call to the function, goes to line 74. Line 74 is the following: let readerWrite = File.CreateText(filename). Nowhere in your code have you closed reader. There is also another problem, you're opening a StreamWriter with File.CreateText. And then you're trying to write to this opened stream with File.WriteAllText, which opens the file, writes to it and closes it. So a bunch of IO handles are floating around there...

To quickly fix it consider the following:

//Getting the filename, needle and replace-word.
System.Console.WriteLine "What is the name of the file?"
let filename = string (System.Console.ReadLine ())

System.Console.WriteLine "What is your needle?"
let needle = string (System.Console.ReadLine ())

System.Console.WriteLine "What you want your needle replaced with?"
let replace = string (System.Console.ReadLine ())   

//Saves the content of the file


//Opens a stream to read the file
//let reader = File.OpenText filename

//Reads the file, and replaces the needle.
let printFile (filename:string) (needle:string) (replace:string) =
  let mutable saveLine = ""
  use reader = File.OpenText filename //use will ensure that the stream is disposed once its out of scope, i.e. the functions exits
  while not(reader.EndOfStream) do
    let line = reader.ReadLine ()
    let lineReplace = line.Replace(needle,replace)
    saveLine <-  saveLine  + lineReplace + "\r\n" //you will need a newline character
    printfn "%s" lineReplace
  saveLine    


//Writes to the file
let editFile filename saveLine =
    File.WriteAllText(filename,saveLine)   //you don't need a stream here, since File.WriteAllText will open, write, then close the file

let saveLine = printFile filename needle replace //read the file into saveLine
editFile filename saveLine       //write saveLine into the file

It does a couple of things:

  1. creates the StreamReader inside the printFile
  2. binds it to reader with use, not let, to ensure it is closed once we don't need it anymore
  3. add a linefeed to the string, since you insist rebuilding a mutable string
  4. encapsulates the mutable saveLine inside the function
  5. passes the needle and replace arguments explicitly
  6. returns a new string to be used in 7.
  7. gets rid of the Streamwriter by using File.WriteAllText and also passes in explicitly the filename and the string to write


来源:https://stackoverflow.com/questions/40617236/how-to-close-a-stream-properly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!