I'm making a log for my program using a StreamWriter to write to a file. I have some code that looks like it works, but its only doing part of what it should.
I made a simple class called Log (code below).
public class Log
{
string path;
StreamWriter fs;
public Log(string fullPathToLogFile)
{
path = fullPathToLogFile;
fs = new StreamWriter(path);
writeLog("Starting log");
}
public void writeLog(string s)
{
fs.WriteLine(DateTime.Now.ToString("dd-MM-yyyy H:mm:ss ") + s);
}
public void closeLog()
{
writeLog("Closing log");
fs.WriteLine(); //add a blank line at the end
fs.Close();
}
}
I made a simple test program that works perfectly. It executes these three lines:
Log l = new Log(#"C:\Users\SADunkerton\Desktop\l.txt");
l.writeLog("testing log");
l.closeLog();
But in my much larger program, where I actually want to use the Log class, all I get is an empty file --no text inside. Its code looks like this:
Log log = new Log(folderPDFs + #"\Log.txt"); //folderPDFs is a parameter of this method--it is a string that is a complete path to a destination folder.
log.writeLog("Beginning conversions");
//do some stuff, including write to the log
log.writeLog("Finished converting. Success = " + success);
Can anyone tell me why the program version of this code isn't working?
I would rewrite your Log class to avoid the close part.
Just open, write and close after finishing the write part
class MySimpleLog
{
private string _filename;
public MySimpleLog(string filename)
{
_filename = filename;
}
public void AppendText(string msg)
{
// Create an instance of StreamWriter to write text to a file.
// The using statement also closes the StreamWriter.
using (StreamWriter sw = new StreamWriter(_filename, true))
{
// Add some text to the file.
sw.WriteLine(msg);
}
}
}
In this way the using statement will close the stream and you don't have to worry about closing it. Indeed closing the stream could be very troublesome if something unexpected happens in your code. (Like an exception that change your code flow).
This is just a starting point to have the code tested and verified, but you could add some more complex logic following the pattern given. For example you could add a constructor with a flag to add a timestamp for everyline (or a flag to add a separator line, or a flag to recreate the file if exists...)
class MySimpleLog
{
private string _filename;
private bool _addtime;
public MySimpleLog(string filename)
{
_filename = filename;
}
public MySimpleLog(string filename, bool addtime)
{
_filename = filename;
_addtime = addtime;
}
public void AppendText(string msg)
{
// Create an instance of StreamWriter to write text to a file.
// The using statement also closes the StreamWriter.
using (StreamWriter sw = new StreamWriter(_filename, true))
{
// Add some text to the file.
msg = (_addtime ? DateTime.Now.ToString() + ": " + msg : msg);
sw.WriteLine(msg);
}
}
}
FINALLY: Keep in mind that specialized log libraries exists well tested and free to use. Perhaps you could invest some of your time in learning them
Log4Net
NLog
1. You do not close the stream in the second example - and what will happen on garbage collection I am not sure. Call the l.closeLog(); in the end to at least get some output. But it is not a good idea. What will happen if some method throws between l.write(); and l.closeLog;. Something not good - file will left opened till GC deals with it.
2. You overwrite the file with each call. Probably what you need is to append the data - http://msdn.microsoft.com/en-us/library/3zc0w663(v=vs.110).aspx or even better change your code to use File.AppendText method:
public void writeLog(string s)
{
using (StreamWriter sw = File.AppendText(path))
{
sw.WriteLine(DateTime.Now.ToString("dd-MM-yyyy H:mm:ss ") + s);
}
}
And remove the closeLog method, because it is unnecessary.
EDIT:
The best idea is to just use stateless standard methods(as pointed by #leppie), that won't leak any resources:
It is File.WriteAllText if you create one logfile for each log instance:
public void writeLog(string s)
{
File.WriteAllText(path,
DateTime.Now.ToString("dd-MM-yyyy H:mm:ss ") + s);
}
or File.AppendAllText if you need to continue already existing logs:
public void writeLog(string s)
{
File.AppendAllText(path,
DateTime.Now.ToString("dd-MM-yyyy H:mm:ss ") + s);
}
Related
I am trying to figure out how to work with files and I got confused due to the amount of different methods.
I have used current way of writting to the file. So far it seems to work fine, but I wonder if I have to add a file closing when the user exits the game?
Another questions is why the file gets created without using File.Create()?
if (!File.Exists(path))
{
File.WriteAllText(path, "Date\t\t| ROM \t\t| Left Hand | Right Hand |\n");
}
The whole code is attached:
public class SavingData : MonoBehaviour
{
public static string path = #"C:\users\Desktop\Game1.txt";
void Start()
{
CreateFile();
}
void CreateFile()
{
if (!File.Exists(path))
{
File.WriteAllText(path, "Date\t\t| ROM \t\t| Left Hand | Right Hand |\n");
}
else
{
string date = "Login date: " + System.DateTime.Now + "\n";
File.AppendAllText(path, date);
}
}
public static void WriteToFile()
{
File.AppendAllText(path, "hellow\n");
}
public static void CloseFile()
{
}
}
The documentation of File.WriteAllText states:
Creates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten.
So this single method does all the things that you thought you didn't do - creating the file and closing it.
If you would like to close the file manually, don't use File.WriteAllText and File.AppendAllText. Use another way of writing to a file, like a StreamWriter.
This is what I would write if I had to use StreamWriter.
public class SavingData : MonoBehaviour
{
public string path = #"C:\users\Desktop\Game1.txt";
private StreamWriter writer;
void Start()
{
CreateFile();
}
void CreateFile()
{
writer = new StreamWriter(path, true);
if (!File.Exists(path))
{
writer.WriteLine("Date\t\t| ROM \t\t| Left Hand | Right Hand |\n");
}
else
{
string date = "Login date: " + System.DateTime.Now + "\n";
writer.WriteLine(date);
}
}
public void WriteToFile()
{
writer.WriteLine("hellow\n");
}
public void CloseFile()
{
writer.Close();
}
}
Actually, if you want more control you need to use FileStream. It gives you more control while writing into files. It allows you to keep the file handle open and just write data without any additional control.
But FileStream also has some type of disadvantages.
From the documentation
When a FileStream object does not have an exclusive hold on its
handle, another thread could access the filehandle concurrently and
change the position of the operating system's file pointer that is
associated with the filehandle. In this case, the cached position in
the FileStream object and the cached data in the buffer could be
compromised. The FileStream object routinely performs checks on
methods that access the cached buffer to ensure that the operating
system's handle position is the same as the cached position used by
the FileStream object.
On the other hands :
System.IO.File contains wrappers around file operations for basic actions such as saving a file, reading a file to lines, etc. It's simply an abstraction over FileStream.
So WriteAllText is the abstraction for over the Create, Save and Close and automatically doing it and you don't need to know each of the implementations.
So the basic answer to your question is: NO, you don't need to manually close file, it will do it automatically.
i try to write line in file when this exists:
My code is next:
string strRuta = "C:\File.txt"
if (!Directory.Exists(strRuta))
Directory.CreateDirectory(strRuta);
string psContenido = "Hola";
if (!(File.Exists(strRuta + strNombreArchivo)))
{
swArchivo = File.CreateText(strRuta + strNombreArchivo);
}
if (!psContenido.ToLower().Contains("error"))
{
swArchivo.WriteLine(psContenido);
swArchivo.Flush();
swArchivo.Close();
swArchivo.Dispose();
File.SetCreationTime(strRuta + strNombreArchivo, DateTime.Now);
}
but when run this program i have a error in WriteLine, i donĀ“t undertand which is the reason, could you help me?
I would like to know how to write in the file(in the next line the word)
There are a couple of problems, I think. First, you're specifying what looks like a file name and creating a directory with that name (not sure if this is intentional or not). Second, you can use the static helper method AppendAllText of the File class to both create the file if it doesn't exist, and to write the contents to the end of the file. It handles all the streamwriter stuff for you, so you don't have to worry about calling close and dispose.
private static void Main(string[] args)
{
string directory = #"C:\private\temp";
string fileName = "MyFile.txt";
string filePath = Path.Combine(directory, fileName);
string fileContents = "This will be written to the end of the file\r\n";
// This will create the directory if it doesn't exist
Directory.CreateDirectory(directory);
// This will create the file if it doesn't exist, and then write the text at the end.
File.AppendAllText(filePath, fileContents);
File.SetCreationTime(filePath, DateTime.Now);
}
I know this is a bit of a "Day one" question, but I'm still having trouble understanding why my Stream writer is writing empty lines after each time it writes
namespace PostFinder
{
class HistorySaver
{
public static void Save(string item, string path)
{
StreamReader sre = new StreamReader(path);
string historyList = sre.ReadToEnd();
sre.Dispose();
StreamWriter sr = new StreamWriter(path);
sr.WriteLine(historyList+sr.NewLine+item);
sr.Dispose();
}
}
}
sr.WriteLine(historyList+sr.NewLine+item);
The .WriteLine() method puts an end-of-line character after the contents you pass to it. If you don't want that character, use .Write().
It looks like all you are wanting to do is append text to a file, so there is really no need to open a streamreader to read in the existing contents and then write them back out with your new content.
You can use the below to do all that you want in one step. If the input path file doesn't exist it will create a new one, and if it already exists it will just append your new item.
namespace PostFinder
{
class HistorySaver
{
public static void Save(string item, string path)
{
File.AppendAllText(path, item + Environment.NewLine);
}
}
}
I am new to programming and I have a question. If I have two functions, one creates a text file and writes into it, while the other opens the same text file and reads from it.
The error I get is:
System.IO.IOException: 'The process cannot access the file '#.txt'
because it is being used by another process.'
I have tried setting seperate timers to each of the functions but it still does not work. I think the best way would be that the function two does not start until function one ends.
Can you help me achieve this?
Thank you very much!
Mike
Source code:
public Form1() {
InitializeComponent();
System.Timers.Timer timerButtona1 = new System.Timers.Timer();
timerButtona1.Elapsed += new ElapsedEventHandler(tickTimera1);
timerButtona1.Interval = 3003;
timerButtona1.Enabled = true;
}
private async void tickTimera1(object source, ElapsedEventArgs e) {
function1();
function2();
}
void function1() {
List<string> linki = new List<string>();
linki.Add("https://link1.net/");
linki.Add("https://link2.net/");
linki.Add("https://link3.net/");
List<string> fileNames = new List<string>();
fileNames.Add("name1");
fileNames.Add("name2");
fileNames.Add("name3");
for (int x = 0; x < fileNames.Count; x++) {
GET(linki[x], fileNames[x]);
//System.Threading.Thread.Sleep(6000);
}
}
async void GET(string link, string fileName) {
var ODGOVOR = await PRENOS_PODATKOV.GetStringAsync(link);
File.WriteAllText(#"C:\Users\...\" + fileName + ".txt", ODGOVOR);
}
void function2() {
string originalText = File.ReadAllText(#"C:\Users\...\fileName.txt", Encoding.Default);
dynamic modifiedText = JsonConvert.DeserializeObject(originalText);
//then i then i read from the same text files and use some data from it..
}
You will have to close the file after editing it.
var myFile = File.Create(myPath);
//myPath = "C:\file.txt"
myFile.Close();
//closes the text file for eg. file.txt
//You can write your reading functions now..
After closing it you can again use it(for reading)
The issue is sometimes file locks don't get released immediately after they are closed.
You can try run a loop to read the file. Inside the loop put a try catch statement and if the file reads successfully break from the loop. Otherwise, wait a few milliseconds and try to read the file again:
string originalText = null;
while (true)
{
try
{
originalText = File.ReadAllText(#"C:\Users\...\fileName.txt", Encoding.Default);
break;
}
catch
{
System.Threading.Thread.Sleep(100);
}
}
after writing your text file, you should close it first before proceeding to your second function:
var myFile = File.Create(myPath);
//some other operations here like writing into the text file
myFile.Close(); //close text file
//call your 2nd function here
Just to elaborate:
public void Start() {
string filename = "myFile.txt";
CreateFile(filename); //call your create textfile method
ReadFile(filename); //call read file method
}
public void CreateFile(string filename) {
var myFile = File.Create(myPath); //create file
//some other operations here like writing into the text file
myFile.Close(); //close text file
}
public void ReadFile(string filename) {
string text;
var fileStream = new FileStream(filename, FileMode.Open,
FileAccess.Read); //open text file
//vvv read text file (or however you implement it like here vvv
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
text = streamReader.ReadToEnd();
}
//finally, close text file
fileStream.Close();
}
The point is, you have to close the FileStream after you are done with any operations with your file. You can do this via myFileStream.Close().
Moreover, File.Create(filename) returns a FileStream object which you can then Close().
Actually this is not a problem of closing/disposing the stream, File.WriteAllText and File.ReadAllText does that internally.
The issue is because a wrong use of the async/await pattern.
GET is async but never awaited, thus causing function1 to finish and move on to function2 before all content was actually written to the file.
The way it is written GET is not awaitable because it is async void which should never be used unless you're dealing with event or really know what you're doing.
So, either remove the use of async/await completely or be async all the way:
Change GET to be awaitable:
async Task GET(string link, string fileName)
await it in the now async function1:
async Task function1()
{
...
for (int x = 0; x < fileNames.Count; x++)
{
await GET(linki[x], fileNames[x]);
//System.Threading.Thread.Sleep(6000);
}
...
await function1 in the Elapsed event:
private async void tickTimera1(object source, ElapsedEventArgs e)
{
await function1();
function2();
}
Create a file and then close it. After can save data into that file.
I did as below.
if (!File.Exists(filePath))
{
File.Create(filePath).Close();
}
File.WriteAllText(filePath, saveDataString)
In one of the unit tests, I had to create a temp file and then remove it after, and I was getting the above error.
None of the answers worked.
Solution that worked was:
var path = $"temp.{extension}";
using (File.Create(path))
{
}
File.Delete(path);
Good day.
I found the example below,
I need to append some more text from another Sub() function.
But i don't know how to.
Could you give me some guide?
THANKS.
using System;
using System.IO;
public class TextToFile
{
private const string FILE_NAME = "MyFile.txt";
public static void Main(String[] args)
{
if (File.Exists(FILE_NAME))
{
Console.WriteLine("{0} already exists.", FILE_NAME);
return;
}
using (StreamWriter sw = File.CreateText(FILE_NAME))
{
sw.WriteLine ("This is my file.");
sw.WriteLine ("I can write ints {0} or floats {1}, and so on.",
1, 4.2);
sw.Close();
}
}
}
If the other function returns the text you want to write, then just write it:
string text = SomeOtherFunction();
sw.Write(text); // or WriteLine to append a newline as well
If you're wanting to append text to an existing file rather than creating a new one, use File.AppendText instead of File.CreateText.
If that's not what you're trying to do, can you clarify the question?
If your function returns a string (or other writable type) you can simply do: sw.WriteLine(theSubINeedToCall());
If you need to process the returned object, you can create a wrapper call and pass the streamWriter to it and then process it i.e:
public void writeOutCustomObject(StreamWriter writer) {
SomeObject theObject = getSomeCustomObject();
writer.WriteLine("ID: " + theObject.ID);
writer.WriteLine("Description: " + theObject.Description);
//.... etc ....
}
add this after Main inside your class
public static void SubFunction(StreamWriter sw)
{
sw.WriteLine("This is more stuff I want to add to the file");
// etc...
}
And then call it in Main like this
using (StreamWriter sw = File.CreateText(FILE_NAME))
{
sw.WriteLine ("This is my file.");
sw.WriteLine ("I can write ints {0} or floats {1}, and so on.", 1,4.2);
MySubFunction(sw); // <-- this is the call
sw.Close();
}