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
}
Related
I'm writing an application in visual studio using c#. I want to check if readalltext finds the file correctly, if not it needs to create the file and put a zero in it. In pseudocode:
if(x=File.ReadAllText("file.txt")==NULL)
{
File.WriteAllText("file.txt", "0");
x=File.ReadAllText("file.txt");
}
How can I do this? Thanks in advance, I tried some google but I may be inputting the wrong keywords
You can check whether a file exists with the File.Exists() method.
string path = "file.txt";
if (!File.Exists(path))
{
File.WriteAllText(path, "0");
}
The problem with using File.Exist() is that there is a risk the file is created or deleted after the check was made. The risk may be small, but may still need to be handled. One way to handle this would be with a try/catch inside a loop:
while (true)
{
try
{
if (!File.Exists(path))
{
File.WriteAllText(path, "0");
return "0";
}
else
{
return File.ReadAllText(path);
}
}
catch (IOException)
{
// try again
}
}
Another way would be to skip ReadAllText and instead open a fileStream. If that succeeds you know you have exclusive access to the file, to either read or write to it:
try
{
using var fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
if (fs.Length == 0)
{
using var sw = new StreamWriter(fs);
sw.Write('0');
return "0";
}
else
{
using var sr = new StreamReader(fs);
return sr.ReadToEnd();
}
}
catch (Exception)
{
// Handle the various types of exception that may occur.
}
I am trying to export the strings in a nested list to a txt or csv file of the users choice and everything seems to be working but when I actually go to check the file after I have exported it the file is absolutely blank. I went and did it on a separate test program to mock my problem and it worked on that program but when I moved the code over it would still not export anything.
This is just my initialized nested list in case its needed.
List<List<string>> aQuestion = new List<List<string>>();
This is the problem area for the code.
static void writeCSV(List<List<string>> aQuestion, List<char> aAnswer)
{
StreamWriter fOut = null;
string fileName = "";
//export questions
//determine if the file can be found
try
{
Console.Write("Enter the file path for where you would like to export the exam to: ");
fileName = Console.ReadLine();
if (!File.Exists(fileName))
{
throw new FileNotFoundException();
}
}
catch (FileNotFoundException)
{
Console.WriteLine("File {0} cannot be found", fileName);
}
//writes to the file
try
{
fOut = new StreamWriter(fileName, false);
//accesses the nested lists
foreach (var line in aQuestion)
{
foreach (var value in line)
{
fOut.WriteLine(string.Join("\n", value));
}
}
Console.WriteLine("File {0} successfully written", fileName);
}
catch (IOException ioe)
{
Console.WriteLine("File {0} cannot be written {1}", fileName, ioe.Message);
}
So if any of you guys can help me with this problem that would be great because it seems like such a small problem but I can't figure it out for the life of me.
It may happen that the buffer was not flushed to the disk. You should dispose the stream writer and it will push everything out to disk:
using (StreamWriter writer = new StreamWriter(fileName, false)) // <-- this is the change
{
//accesses the nested lists
foreach (var line in aQuestion)
{
foreach (var value in line)
{
writer.WriteLine(string.Join("\n", value));
}
}
}
On a more elaborate level, streams that may cause performance loss are normally buffered. File streams are definitely buffered, because it would be very inefficient to push each separate piece of data to the IO immediately.
When you're working with file streams, you can flush their content explicitly using the StreamWriter.Flush() method - that is useful if you want to debug code and wish to see how far it has gone writing the data.
However, you normally do not flush the stream yourself but just let its internal mechanisms choose the best moment to do that. Instead, you make sure to dispose the stream object, and that will force buffer to be flushed before closing the stream.
Use this simple method instead, it is much easier and it will take care of creating and disposing StreamWriter.
File.WriteAllLines(PathToYourFile,aQuestion.SelectMany(x=>x));
More reference on File.WriteAllLines Here
Also, in your code your not disposing StreamWrite. Enclose it in a Using block. Like this..
using(var writer = new StreamWriter(PathToYourFile,false)
{
//Your code 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.
Hi everyone beginner here looking for some advice with a program I'm writing in C#. I need to be able to open a text document, read the first line of text (that is not blank), save this line of text to another text document and finally overwrite the read line with an empty line.
This is what I have so far, everything works fine until the last part where I need to write a blank line to the original text document, I just get a full blank document. Like I mentioned above I'm new to C# so I'm sure there is an easy solution to this but I can't figure it out, any help appreciated:
try
{
StreamReader sr = new StreamReader(#"C:\Users\Stephen\Desktop\Sample.txt");
line = sr.ReadLine();
while (line == "")
{
line = sr.ReadLine();
}
sr.Close();
string path = (#"C:\Users\Stephen\Desktop\new.txt");
if (!File.Exists(path))
{
File.Create(path).Dispose();
TextWriter tw = new StreamWriter(path);
tw.WriteLine(line);
tw.Close();
}
else if (File.Exists(path))
{
TextWriter tw = new StreamWriter(path, true);
tw.WriteLine(line);
tw.Close();
}
StreamWriter sw = new StreamWriter(#"C:\Users\Stephen\Desktop\Sample.txt");
int cnt1 = 0;
while (cnt1 < 1)
{
sw.WriteLine("");
cnt1 = 1;
}
sw.Close();
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.Message);
}
finally
{
Console.WriteLine("Executing finally block.");
}
else
Console.WriteLine("Program Not Installed");
Console.ReadLine();
Unfortunately, you do have to go through the painstaking process of rewriting the file. In most cases, you could get away with loading it into memory and just doing something like:
string contents = File.ReadAllText(oldFile);
contents = contents.Replace("bad line!", "good line!");
File.WriteAllText(newFile, contents);
Remember that you'll have to deal with the idea of line breaks here, since string.Replace doesn't innately pay attention only to whole lines. But that's certainly doable. You could also use a regex with that approach. You can also use File.ReadAllLines(string) to read each line into an IEnumerable<string> and test each one while you write them back to the new file. It just depends on what exactly you want to do and how precise you want to be about it.
using (var writer = new StreamWriter(newFile))
{
foreach (var line in File.ReadAllLines(oldFile))
{
if (shouldInsert(line))
writer.WriteLine(line);
}
}
That, of course, depends on the predicate shouldInsert, but you can modify that as you see so fit. But the nature of IEnumerable<T> should make that relatively light on resources. You could also use a StreamReader for a bit lower-level of support.
using (var writer = new StreamWriter(newFile))
using (var reader = new StreamReader(oldFile))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (shouldInsert(line))
writer.WriteLine(line);
}
}
Recall, of course, that this could leave you with an extra, empty line at the end of the file. I'm too tired to say that with the certainty I should be able to, but I'm pretty sure that's the case. Just keep an eye out for that, if it really matters. Of course, it normally won't.
That all said, the best way to do it would be to have a bit of fun and do it without wasting the memory, by writing a function to read the FileStream in and write out the appropriate bytes to your new file. That's, of course, the most complicated and likely over-kill way, but it'd be a fun undertaking.
See: Append lines to a file using a StreamWriter
Add true to the StreamWriter constructor to set it to "Append" mode. Note that this adds a line at the bottom of the document, so you may have to fiddle a bit to insert or overwrite it at the top instead.
And see: Edit a specific Line of a Text File in C#
Apparently, it's not that easy to just insert or overwrite a single line and the usual method is just to copy all lines while replacing the one you want and writing every line back to the file.