如何正确关闭流?

How to close a stream properly?

我被分配了一个任务来编写一个程序,该程序将:

  1. 打开文件。

  2. 阅读内容

  3. 用另一个词替换特定词。

  4. 保存对文件的更改。

我确信我的代码可以打开、阅读和替换文字。当我添加 "Save the changes to the file" - 部分时出现问题。这是代码:

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)

我收到错误消息 "Sharing violation on path...",这让我相信阅读流没有正确关闭。我试过调整我的代码结构,并为 .NET 库尝试了不同的东西,但我总是收到相同的错误消息。非常感谢任何帮助。

流通常通过调用 Stream.Close() 或释放它们来关闭。

System.IO 具有读取或写入完整文件 from/to 行数组的方法。这会将操作缩短为如下所示:

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

您使用的是什么文档?查看 the MSDN documentation for System.IO 和类似的 MSDN 文档,了解 .NET/the CLR 中的各种内容;这些可以快速回答此类问题。

我保留了你的大部分原始代码,尽管它不是很地道。如果您将 use 与一次性资源一起使用,.NET 将在您之后进行清理。参见示例 F# Docs and Fun&Profit, the latter also has a nice section on Expressions and syntax

如果你执行你的代码,你应该得到 System.IO.IOException:

Unhandled Exception: System.IO.IOException: The process cannot access the file 'C:\Users\xcs\Documents\Visual Studio 2015\Projects\Whosebug6\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\Whosebug6\ConsoleApplication11\Program.fs:line 74
at Program.main(String[] argv) in C:\Users\xcs\Documents\Visual Studio 2015\Projects\Whosebug6\ConsoleApplication11\Program.fs:line 83

它从第 83 行开始,这是对函数的调用,转到第 74 行。第 74 行如下:let readerWrite = File.CreateText(filename)。您的代码中没有任何地方关闭 reader。还有一个问题,你用File.CreateText打开了一个StreamWriter。然后你试图用 File.WriteAllText 写入这个打开的流,这会打开文件,写入并关闭它。所以一堆 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


//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

它做了几件事:

  1. printFile
  2. 中创建 StreamReader
  3. use 将它绑定到 reader,而不是让它在我们不再需要它时关闭它
  4. 在字符串中添加一个换行符,因为您坚持重建一个可变字符串
  5. 将可变的saveLine封装在函数
  6. 通过针并显式替换参数
  7. returns 将在 7 中使用的新字符串。
  8. 通过使用 File.WriteAllText 摆脱了 Streamwriter 并且还明确传递了文件名和要写入的字符串