I've apparently worked myself into a bad coding habit. Here is an example of the code I've been writing:
using(StreamReader sr = new StreamReader(File.Open("somefile.txt", FileMode.Open)))
{
//read file
}
File.Move("somefile.txt", "somefile.bak"); //can't move, get exception that I the file is open
I thought that because the using clause explicitly called Close() and Dispose() on the StreamReader that the FileStream would be closed as well.
The only way I could fix the problem I was having was by changing the above block to this:
using(FileStream fs = File.Open("somefile.txt", FileMode.Open))
{
using(StreamReader sr = new StreamReader(fs))
{
//read file
}
}
File.Move("somefile.txt", "somefile.bak"); // can move file with no errors
Should closing the StreamReader by disposing in the first block also close the underlying FileStream? Or, was I mistaken?
Edit
I decided to post the actual offending block of code, to see if we can get to the bottom of this. I am just curious now.
I thought I had a problem in the using clause, so I expanded everything out, and it still can't copy, every time. I create the file in this method call, so I don't think anything else has a handle open on the file. I've also verified that the strings returned from the Path.Combine calls are correct.
private static void GenerateFiles(List<Credit> credits)
{
Account i;
string creditFile = Path.Combine(Settings.CreditLocalPath, DateTime.Now.ToString("MMddyy-hhmmss") + ".credits");
StreamWriter creditsFile = new StreamWriter(File.Open(creditFile, FileMode.Create));
creditsFile.WriteLine("code\inc");
foreach (Credit c in credits)
{
if (DataAccessLayer.AccountExists(i))
{
string tpsAuth = DataAccessLayer.GetAuthCode(i.Pin);
creditsFile.WriteLine(String.Format("{0}{1}\t{2:0.00}", i.AuthCode, i.Pin, c.CreditAmount));
}
else
{
c.Error = true;
c.ErrorMessage = "NO ACCOUNT";
}
DataAccessLayer.AddCredit(c);
}
creditsFile.Close();
creditsFile.Dispose();
string dest = Path.Combine(Settings.CreditArchivePath, Path.GetFileName(creditFile));
File.Move(creditFile,dest);
//File.Delete(errorFile);
}
Yes, StreamReader.Dispose closes the underlying stream (for all public ways of creating one). However, there's a nicer alternative:
using (TextReader reader = File.OpenText("file.txt"))
{
}
This has the added benefit that it opens the underlying stream with a hint to Windows that you'll be accessing it sequentially.
Here's a test app which shows the first version working for me. I'm not trying to say that's proof of anything in particular - but I'd love to know how well it works for you.
using System;
using System.IO;
class Program
{
public static void Main(string[] args)
{
for (int i=0; i < 1000; i++)
{
using(StreamReader sr = new StreamReader
(File.Open("somefile.txt", FileMode.Open)))
{
Console.WriteLine(sr.ReadLine());
}
File.Move("somefile.txt", "somefile.bak");
File.Move("somefile.bak", "somefile.txt");
}
}
}
If that works, it suggests that it's something to do with what you do while reading...
And now here's a shortened version of your edited question code - which again works fine for me, even on a network share. Note that I've changed FileMode.Create to FileMode.CreateNew - as otherwise there could still have been an app with a handle on the old file, potentially. Does this work for you?
using System;
using System.IO;
public class Test
{
static void Main()
{
StreamWriter creditsFile = new StreamWriter(File.Open("test.txt",
FileMode.CreateNew));
creditsFile.WriteLine("code\\inc");
creditsFile.Close();
creditsFile.Dispose();
File.Move("test.txt", "test2.txt");
}
}
Note - your using blocks do not need to be nested in their own blocks - they can be sequential, as in:
using(FileStream fs = File.Open("somefile.txt", FileMode.Open))
using(StreamReader sr = new StreamReader(fs))
{
//read file
}
The order of disposal in this case is still the same as the nested blocks (ie, the StreamReader will still dispose before the FileStream in this case).
I would try to use FileInfo.Open() and FileInfo.MoveTo() instead of File.Open() and File.Move(). You could also try to use FileInfo.OpenText(). But these are just suggestions.
Is there any possibility that something else has a lock to somefile.txt?
A simple check from a local (to the file) cmd line
net files
may well give you some clues if anything else has a lock.
Alternatively you can get something like FileMon to take even more details, and check that your app is releasing properly.
Since this doesn't seem to be a coding issue, I'm going to put my syadmin hat on and offer a few suggestions.
Virus scanner on either the client or server that's scanning the file as it's created.
Windows opportunistic locking has a habit of screwing things up on network shares. I recall it being mostly an issue with multiple read/write clients with flat file databases, but caching could certainly explain your problem.
Windows file open cache. I'm not sure if this is still a problem in Win2K or not, but FileMon would tell you.
Edit: If you can catch it in the act from the server machine, then Sysinternal's Handle will tell you what has it open.
Related
I have my desired txt files which I want to use as TextAssets. I need these files to be usable at runtime by my other scripts. Now the issue is that I can not figure out a way to make these things work.
I know that I should be using the Assets/Resources or the Streaming Assets folder but for some reason things are not working properly. Is there a way to incorporate it all with StreamWriters and Filestreams? What about TextAssets assigned in Unity Editor, can those also be setup as Streaming?
Some examples of code that uses my assets:
public void TaskOnClick() //getting multi-values
{
string filename = "Assets/Resources/TempoText/multi-export.txt";
using (StreamWriter writeFile = new StreamWriter(filename, false))
{
foreach (string inputJson in File.ReadLines("Assets/Resources/TempoText/multi-import.txt"))
{
string temperature = GetTemperatureByRegex(inputJson);
Debug.Log(temperature);
writeFile.AutoFlush = true;
Console.SetOut(writeFile);
writeFile.WriteLine(temperature.ToString());
}
}
File.Copy("Assets/Resources/TempoText/multi-export.txt", "Assets/Resources/multi-export.txt", true);
}
//or
FileStream filestream = new FileStream("Assets/Resources/TempoText/multi-import.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
var writeFile = new StreamWriter(filestream);
{
var document = collection.Find(new BsonDocument()).Sort(sort).Limit(limit: limit).ForEachAsync(d => Console.WriteLine(d)); //displays last 10 entries
Debug.Log(document.ToString());
writeFile.AutoFlush = true;
Console.SetOut(writeFile);
writeFile.Write(document.ToString());
}
All help greatly appreciated, I've basically messed up big time since I only found out about this now when I built everything as is...
Edit: got the streamwriters to do everything nicely with Application.persistentDataPath! Now stuck with a problem that I already struggled with - how to assign a TextAsset to get the file from a fixed path...
public TextAsset textFile;
Wondering how to set this to get it's .txt from Application.persistentDataPath
Application.persistentDataPath is what was needed all along.
Something nobody ever mentioned wherever I looked around. Hope somebody will be able to find the correct way using this mess of a question and lackluster answer.
I've been searching for some time and I haven't found an answer to this, so I thought I'd ask instead and sorry if it infact has been asked already :)
I've been trying to create a small program that helps one of our people in another department, and while the program runs quite fine, I find myself unable to delete, move or write to files that have been accessed by the program for as long as the program runs.
I let him create simple configurations for calculations he needs to make, but I can't let him remove or edit configuration the program has already loaded in.
The code I have which accesses the files
configs = new List<Config>();
DirectoryInfo dir = new DirectoryInfo(Environment.CurrentDirectory);
foreach (FileInfo file in dir.GetFiles("*.csv"))
{
StreamReader reader = new StreamReader(file.FullName);
Config currentConfig = new Config(file.Name);
Code runs here...
}
The Config class is a simple class I've created and it holds information from the csv's that's loaded in
The rest of the code doesn't touch the files.
Any help is appreciated.
As others has pointed out, you should use using statement (MSDN reference) but I would like to explain the 'why part' a little bit.
The reason behind this is a fact, that you open a file, read it and keep it open. I love C# for its garbage collection, but in this case it doesn't kicks in by itself. You have to dispose of Streams (and other IDisposable objects) yourself. You can do this by a) calling reader.Close() and reader.Dispose() when done with file, or b) let C# do it for you by using the using statement as
using (StreamReader reader = new StreamReader(file.FullName)) {
//Do your stuff
}
What happens here is when program exits using statement the reader is disposed of by the "end" of using statement. This is also true in a case of throwing Exceptions, what the the first case does not handle. In a practice using it behaves as a try { ... } finally { ... } block of sorts.
You shoul use 'using' block
foreach (FileInfo file in dir.GetFiles("*.csv"))
{
using(StreamReader reader = new StreamReader(file.FullName))
{
Config currentConfig = new Config(file.Name);
// Code runs here...
}
}
You have to Close an Dispose your StreamReader:
configs = new List<Config>();
DirectoryInfo dir = new DirectoryInfo(Environment.CurrentDirectory);
foreach (FileInfo file in dir.GetFiles("*.csv"))
{
using(StreamReader reader = new StreamReader(file.FullName))
{
Config currentConfig = new Config(file.Name);
Code runs here...
} // reader will be disposed here
}
I'm working on a gradebook program assignment for a class; the details aren't so important, except to know that I need to be able to save a file and recall it later. I know how to serialize, deserialize, etc, and everything's good there. But the problem comes when I try to save. I'm a bit new to the whole saving data scene, and I don't exactly know the techniques, but what I have seems like it should work - except that every time I try it, I get an error.
private static void Save (IList<GradebookEntry> gradebook) {
Console.WriteLine ("Saving changes. Please wait...");
using (IsolatedStorageFile stored = IsolatedStorageFile.GetStore (IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null)) {
try {
using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream ("Temp.utc", FileMode.Create, stored)) {
BinaryFormatter bform = new BinaryFormatter ();
bform.Serialize (isoStream, gradebook);
string[] s = stored.GetDirectoryNames ();
stored.DeleteFile ("Gradebook.utc");
stored.MoveFile ("Temp.utc", "Gradebook.utc"); // #!!
}
Console.WriteLine ("Changes saved.");
}
catch (Exception ex) {
Console.WriteLine ("Saving failed. Reason: {0}", ex.Message);
}
finally {
if (stored.FileExists("Temp.utc")) {
stored.DeleteFile ("Temp.utc");
}
}
}
}
The marked line, where I try to move the file, is where I have problems. Everything else works fine, but when I reach that line, it throws an IsolatedStorageException with the message "Operation not permitted". I've looked all over, I've studied MSDN, I've searched all the places I can, but I can't figure out what the problem is. It's probably just something I overlooked, but I'm tearing my hair out here and I could use a bit of help. Thanks.
To expand on archon's comment, the move operation fails because it's inside the using block. Changing the code as follows fixes the problem.
using (IsolatedStorageFileStream isoStream =
new IsolatedStorageFileStream("Temp.utc", FileMode.Create, stored))
{
BinaryFormatter bform = new BinaryFormatter();
bform.Serialize(isoStream, gradebook);
}
stored.DeleteFile("Gradebook.utc");
stored.MoveFile("Temp.utc", "Gradebook.utc");
The reason why it fails is that the using block has the file Temp.utc open, and an open file can't be moved. Once execution leaves the using block, the Dispose method is called on isoStream which causes it to close the open file.
I am trying to write a log file, but it constantly says "File being used by another process". Here's my code:
//_logFile = "system.log"
if(!File.Exists(Path.Combine("logs", _logFile)))
{
File.Create(Path.Combine("logs", _logFile)).Close();
sw = File.AppendText(Path.Combine("logs", _logFile));
}
else
{
sw = File.AppendText(Path.Combine("logs", _logFile));
}
When I run it, it points to the File.Create(Path.Combine("logs", _logFile)).Close() line and gives me the error.
Edit:
I changed if(!File.Exists(_logFile)) to if(!File.Exists(Path.Combine("logs", _logFile))) but I still get the same error.
Assuming you don't need access to this stream outside the context of this method, I'd refactor your code to this:
var filePath = Path.Combine("logs", _logFile);
using (var sw = File.AppendText(filePath))
{
//Do whatever writing to stream I want.
sw.WriteLine(DateTime.Now.ToString() + ": test log entry");
}
This way, no matter what happens inside the using block, you know the file will be closed so you can use it again later.
Note that File.AppendText will create the file if it doesn't already exist, so File.Create is not needed.
I've been trying to come up with a way for my code to open a file or create one (if my given file name is non-existent). Afterwards, it will run a program that will end up creating an array and I want the contents of that array to be converted into string and appended into the file that I am creating and opening. I've got everything right except for the 'Append' part. It say in the end that the "Object reference not set to an instance of an object." Can you please enlighten me on this one? Help will be much appreciated.
try
{
FileStream fs = new FileStream("inventory.ini", FileMode.OpenOrCreate, FileAccess.Read);
StreamReader reader = new StreamReader(fs);
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] data = line.Split('|');
int code = int.Parse(data[0]);
string name = data[1];
double price = double.Parse(data[2]);
Item item = new Item(code, name, price);
app.array[inventoryCount++] = item;
}
reader.Close();
fs.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
app.Run();
try
{
FileStream fs = new FileStream("inventory.ini", FileMode.Append, FileAccess.Write);
StreamWriter writer = new StreamWriter(fs);
foreach (Item item in app.array)
{
writer.WriteLine(item.Code + "|" + item.Name + "|" + item.Price);
}
writer.Close();
fs.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
You can use another constructor of StreamWriter, that allows appending, and then write like this:
StreamWriter writer = new StreamWriter("inventory.ini", true);
I never used FileStream in my apps, but StreamWriter has been quite reliable. You can also switch to Using statement, then you don't need to Close().
Also I suggest switching to lists, then you will always have the exact amount of items you need inside app.array (which btw needs a better name). So this:
app.array[inventoryCount++] = item;
will change to something like this:
app.list.Add(item);
Aside from memory management headache relief, you no longer need inventoryCount variable, since you can get this value from list.Count;
The general approach here is to minimize amount of code you need to write, for the same amount of functionality. Then you have no place for the error to lurk.
catch (Exception e)
{
Console.WriteLine(e.Message);
}
You are digging yourself a pretty deep hole with exception handling like this. A hard rule in catching an exception is that you restore the state of your program when you handle it. You don't. In particular, you are forgetting to close the file. This then goes wrong, later, when you try to open the file again to write. The exception message is misleading, unfortunately, talking about another process having the file already opened. Not the case, it is your process that still has the file opened.
There are plenty of countermeasures against this failure. You should be using the using statement to ensure the file is closed even if there's an exception. And you'll need to fix your EndOfStream test, it isn't accurate on text files, use a while(true) loop and break when ReadLine() returns null. Which solves the original problem.
But the real fix is to not hide an inconvenient truth. Allowing your program to continue running when a config file is broken just begets more trouble when it doesn't do what you hope it does. And you can't tell because the message you write to the console was scrolled off the screen. Very hard to diagnose.
Remove the try/catch from this code. Now you get to address the real problem.
Note that you can also just use File.AppendText() to open a StreamWriter in append mode.
You should also use using instead of .Close() to close the stream - then it will work even if an exception occurs.
So your code would look more like this:
try
{
using (var writer = File.AppendText("inventory.ini"))
{
foreach (Item item in app.array)
{
if (item != null)
writer.WriteLine(item.Code + "|" + item.Name + "|" + item.Price);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Why don't you use using statement
using (FileStream fs = new FileStream("inventory.ini", FileMode.OpenOrCreate, FileAccess.Read))
using (StreamReader reader = new StreamReader(fs))
{
// do stuff
}