I need to create a file, write one line of text in the file and then delete the file and estimate how long it will take to do it.
Unfortunately, I am running in couple of problems, first I cannot write in the file, it succesfully creates it but nothing is written to it.
Secondly, I cannot delete the file because it has been used by another process.
Please help.
I have been trying to delete it for quite some time.
I have also tried wrapping it in usings, to no avail.
Writing to the file is the same situation. I even changed it so the file ends in .txt but that does not make any difference.
public static void ProcessFile(string path)
{
string fullpath = path;
int lastBracket = path.LastIndexOf("\\");
// the filename does not contain .html so it can be named to .txt
string newFileName = path.Substring(lastBracket + 1, path.Length - lastBracket - 6) + " hrefcount.txt";
string newPath = Path.Combine(fullpath.Substring(0, lastBracket), newFileName);
Console.WriteLine(newPath);
int counter = 0;
foreach (var line in File.ReadAllLines(path))
{
if (line.Contains("href="))
{
counter++;
}
}
var fileCreated = File.CreateText(newPath);
fileCreated.WriteLine("The number of times href appears is " + counter);
Console.WriteLine();
File.Delete(newPath);
}
File created, nothing written to it, unable to delete due to has been used by another process.
Instead of File.CreateText() use File.WriteAllText(path, content). It writes the text and then closes the file allowing you to delete it if necessary
Instead of the following
var fileCreated = File.CreateText(newPath);
fileCreated.WriteLine("The number of times href appears is " + counter);
You may write
File.WriteAllText(newPath, $"The number of times href appears is {counter}");
Refer documentation here
The issue with your approach is that CreateText() is used to write to a stream. But in your case, it is not necessary since you're writing all the text at once to the file and that text is small in size.
The cause of your error is the fact that you don't close and dispose the variable fileCreated. This, is a FileStream and until you close and dispose this variable the file is not available to anyone, even your own code that has opened the file.
So the first thing to do is
using (var fileCreated = File.CreateText(newPath))
{
fileCreated.WriteLine("The number of times href appears is " + counter);
}
The using block ensure the proper disposal of the variable.
However there are other parts of your code that you can simplify
public static void ProcessFile(string path)
{
string folder = Path.GetDirectoryName(path);
string file = Path.GetFileName(path);
// Keep the first 6 characters from the source file?
string newFile = file.Substring(0, 6) + " hrefcount.txt";
string newPath = Path.Combine(folder, newFile);
// A single line to retrieve your counter thanks to IEnumerables and Linq
int counter = File.ReadLines(path).Count(x => x.Contains("href="));
// Create, but dispose also the file
using (var fileCreated = File.CreateText(newPath))
{
fileCreated.WriteLine("The number of times href appears is " + counter);
}
// Now you should be free to delete the file
File.Delete(newPath);
}
I cannot delete the file because it has been used by another process.
Probably you're not disposed your files after creating. To do that, you should additionally use FileStream.Dispose method:
File.Create(path).Dispose();
estimate how long it will take to do it
You can use System.Diagnostics.Stopwatch class to do that:
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
/*
do the magic
*/
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
You can use File.WriteAllText method instead of File.CreateText to write your text to file:
File.WriteAllText(path, myText);
Remember that since the .NET 4 you can use this method with array or List<T> too instead of string.
File.Create() supports Dispose method which help you to release that file resource to perform further operations
To perform operations on file follow below steps:
Create file and free up the resource using Dispose().
File.Create(newPath).Dispose();
or Use StreamWriter to create file and write text to it.
using( StreamWriter sw = new StreamWriter(newPath, true)
{
sw.Write($"The number of times href appears is {counter}"); //Used string interpolation
}
StreamWriter calls Dispose() function to release file resource.
When Writer release control over file, then you will not face issue related to I cannot delete the file because it has been used by another process.
Now you can delete file using,
File.Delete(newPath);
MSDN : IDisposable.Dispose Method
Performs application-defined tasks associated with freeing, releasing,
or resetting unmanaged resources.
I am trying to get the text of a file in C# (config) and to put it into fields. The code itself works, but I need to close the file stream to open a new one futher in the code, but don't know where to put the Close() tag. Can someone help me?
string documentspath = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string rpath = documentspath + #"\Luminous\remember.ini";
if (File.Exists(rpath))
{
try
{
string text = File.ReadAllText(rpath);
string path = text.Split('|')[0];
string process = text.Split('|')[1];
string loadLib = text.Split('|')[2];
pathBox.Text = path;
processBox.Text = process;
if (loadLib == "True")
{
loadLibrary.Checked = true;
}
else
{
manualMap.Checked = true;
}
} catch
{
MessageBox.Show("Config file corrupted. Remembered data has been lost and deleted.");
File.Delete(rpath);
}
} else
{
MessageBox.Show("No config file loaded. Welcome, " + Environment.UserName + ".");
}
EDIT: Here's the code from where I'm opening another file stream.
string documentspath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if (!Directory.Exists(documentspath + #"\Luminous")) {
Directory.CreateDirectory(documentspath + #"\Luminous");
} else {
string rpath = documentspath + #"\Luminous\remember.ini";
if (!File.Exists(rpath)) {
File.Create(rpath);
File.WriteAllText(rpath, pathBox.Text + "|" + processBox.Text + "|" + loadlibcheck + "|" + manualmapcheck);
} else {
File.WriteAllText(rpath, string.Empty);
File.WriteAllText(rpath, pathBox.Text + "|" + processBox.Text + "|" + loadlibcheck + "|" + manualmapcheck);
}
}
You don't need to close File when using File.ReadAllText().
From MSDN:
File.ReadAllText Method Opens a text file, reads all lines of the file, and then closes the file.
Error appears because of this line:
File.Create(rpath);
Try:
using(File.Create(rpath)) {}
File.Create method actually return FileStream, which have to be disposed. Some info about it can be found on the documentation.
In section "Return Value":
Type: System.IO.FileStream A FileStream that provides read/write
access to the file specified in path.
In section "Remarks":
The FileStream object created by this method has a default FileShare value of None; no other process or code can access the created file until the original file handle is closed.
However, as Chris Dunaway mentioned, there is no need to use File.Create at all, as according to documentation WriteAllText :
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.
You have two good options:
On the finally clause of your try/catch block.
Change your code to use using statement for your file stream.
You don't have to close anything. File.ReadAllText reads and closes a stream internally. So you can safely invoke another File.ReadAllText if you need to.
As the title suggests, I'm attempting to read and write to a file at the same time. I have researched this topic but the answers I have found don't seem to work for me because of the circumstances in my program. I am using multiple FileSystemWatchers to track a large amount of files that are constantly passing through a flow in my network. As the files pass through each part of my flow, a text file is updated(one text file per spot in the flow) that marks the name of the file and the time it was created within the folder. It is unpredictable when files might be passing through, and when they might be writing to the tracker text files. My goal is to be able to read and write to the file simultaneously, in case a user attempts to read to from a text file that is being written to at the exact same time. How would I accomplish this?
//Write to File
private void WriteToFile(string info,string path,string tr)
{
if (!File.Exists(path+#"\"+#tr))
{
var myFile =
File.Create(path + #"\" + #tr);
myFile.Close();
TextWriter tw = new StreamWriter(path + #"\" + #tr, true);
tw.WriteLine(info,true);
tw.Close();
}
else if (File.Exists(path + #"\" + #tr))
{
TextWriter tw = new StreamWriter(path + #"\" + #tr, true);
tw.WriteLine(info);
tw.Close();
}
}
The circumstances you are alluding to seem to say that while multiple attempts can be made to read/write the file at a given time, you still want to ensure that the operations are performed one after another in the correct order that the read or writes got called.
One simple method of ensuring that the read and write operations are synchronized would be to just put a lock or Monitor around the methods. Try the following code for your write method:
private readonly object _locker = new object();
// write the file
private void WriteToFile(string info, string path, string tr)
{
Monitor.Enter(this._locker);
try
{
if (!File.Exists(path + #"\" + #tr))
{
var myFile =
File.Create(path + #"\" + #tr);
myFile.Close();
TextWriter tw = new StreamWriter(path + #"\" + #tr, true);
tw.WriteLine(info, true);
tw.Close();
}
else if (File.Exists(path + #"\" + #tr))
{
TextWriter tw = new StreamWriter(path + #"\" + #tr, true);
tw.WriteLine(info);
tw.Close();
}
}
finally
{
Monitor.Exit(this._locker);
}
}
Then, I would use a very similar construct for reading the file.
// read the file
private string ReadFile(string path)
{
Monitor.Enter(this._locker);
try
{
// read the file here...
}
finally
{
Monitor.Exit(this._locker);
}
}
What the Monitor will do is ensure that the file will not be read until an on-going write operation is complete (and vice-versa). This will ensure that you will not get the old data when you read it, and you will also not over-write the new data (which has not yet been read). This method verifies the integrity of your files at all times.
I'm writing a function which is going to serialize class and save it to file, some classes must be saved in a different folder. I'm using Unity and C#. Here's my code:
public void save<T>(T data, string fileName) where T : class{
if (fileName == "")
Debug.Log ("Empty file path");
FileStream file = null;
try{
if(fileName.IndexOf("/") > 0){
string[] strDirName = fileName.Split(new char[] {'/'});
string dirName = strDirName[0];
if(!Directory.Exists(Application.persistentDataPath + dirName)){
Directory.CreateDirectory(Application.persistentDataPath + "/" + dirName);
}
}
file = File.Create(constructFilePath(fileName));
string a = constructFilePath(fileName);
binFormatter.Serialize(file, data);
Debug.Log ("File saved succesfully" + fileName);
}catch(IOException e){
Debug.Log(e.ToString());
}finally{
if(file != null)
file.Close();
}
}
string constructFilePath(string fileName){
return Path.Combine(Application.persistentDataPath, fileName);
}
I have no idea why it's saving files as folder, this happens since I added this line to construct constructFilePath
if(fileName[0] != "/")
fileName = "/" + fileName;
But without this file it's creating different folder. It's concatenating the Application.persistentDataPath with the folder name and creates the file there
so if my persistentDataPath = C:/Users/User/AppData/LocalLow/DefaultCompany/TestGame and I want to store the file inside this folder in folder a and store file b in it
C:/Users/User/AppData/LocalLow/DefaultCompany/TestGame/a/b
it creates folder with name TestGamea and stores b inside it
C:/Users/User/AppData/LocalLow/DefaultCompany/TestGamea/b
You are evaluating one thing and performing something different here:
if(!Directory.Exists(Application.persistentDataPath + dirName)){
Directory.CreateDirectory(Application.persistentDataPath + "/" + dirName);
}
Change this to:
if(!Directory.Exists(Path.Combine(Application.persistentDataPath, dirName))){
Directory.CreateDirectory(Path.Combine(Application.persistentDataPath, dirName));
}
Like Eric said, use Path.Combine. it will reliably combine path parts and ensure you get the same result every time so you don't have to worry about string manipulation.
I need to generate a unique temporary file with a .csv extension.
What I do right now is
string filepath = System.IO.Path.GetTempFileName().Replace(".tmp", ".csv");
However, this doesn't guarantee that my .csv file will be unique.
I know the chances I ever got a collision are very low (especially if you consider that I don't delete the .tmp files), but this code doesn't looks good to me.
Of course I could manually generate random file names until I eventually find a unique one (which shouldn't be a problem), but I'm curious to know if others have found a nice way to deal with this problem.
Guaranteed to be (statistically) unique:
string fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
(To quote from the wiki article on the probabilty of a collision:
...one's annual risk of being hit by a
meteorite is estimated to be one
chance in 17 billion [19], that means
the probability is about 0.00000000006
(6 × 10−11), equivalent to the odds of
creating a few tens of trillions of
UUIDs in a year and having one
duplicate. In other words, only after
generating 1 billion UUIDs every
second for the next 100 years, the
probability of creating just one
duplicate would be about 50%. The
probability of one duplicate would be
about 50% if every person on earth
owns 600 million UUIDs
EDIT: Please also see JaredPar's comments.
Try this function ...
public static string GetTempFilePathWithExtension(string extension) {
var path = Path.GetTempPath();
var fileName = Path.ChangeExtension(Guid.NewGuid().ToString(), extension);
return Path.Combine(path, fileName);
}
It will return a full path with the extension of your choice.
Note, it's not guaranteed to produce a unique file name since someone else could have technically already created that file. However the chances of someone guessing the next guid produced by your app and creating it is very very low. It's pretty safe to assume this will be unique.
public static string GetTempFileName(string extension)
{
int attempt = 0;
while (true)
{
string fileName = Path.GetRandomFileName();
fileName = Path.ChangeExtension(fileName, extension);
fileName = Path.Combine(Path.GetTempPath(), fileName);
try
{
using (new FileStream(fileName, FileMode.CreateNew)) { }
return fileName;
}
catch (IOException ex)
{
if (++attempt == 10)
throw new IOException("No unique temporary file name is available.", ex);
}
}
}
Note: this works like Path.GetTempFileName. An empty file is created to reserve the file name. It makes 10 attempts, in case of collisions generated by Path.GetRandomFileName();
You can also alternatively use System.CodeDom.Compiler.TempFileCollection.
string tempDirectory = #"c:\\temp";
TempFileCollection coll = new TempFileCollection(tempDirectory, true);
string filename = coll.AddExtension("txt", true);
File.WriteAllText(Path.Combine(tempDirectory,filename),"Hello World");
Here I used a txt extension but you can specify whatever you want. I also set the keep flag to true so that the temp file is kept around after use. Unfortunately, TempFileCollection creates one random file per extension. If you need more temp files, you can create multiple instances of TempFileCollection.
The MSDN documentation for C++'s GetTempFileName discusses your concern and answers it:
GetTempFileName is not able to guarantee that the file name is unique.
Only the lower 16 bits of the uUnique parameter are used. This limits GetTempFileName to a maximum of 65,535 unique file names if the lpPathName and lpPrefixString parameters remain the same.
Due to the algorithm used to generate file names, GetTempFileName can perform poorly when creating a large number of files with the same prefix. In such cases, it is recommended that you construct unique file names based on GUIDs.
Why not checking if the file exists?
string fileName;
do
{
fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));
You can also do the following
string filepath = Path.ChangeExtension(Path.GetTempFileName(), ".csv");
and this also works as expected
string filepath = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");
How about:
Path.Combine(Path.GetTempPath(), DateTime.Now.Ticks.ToString() + "_" + Guid.NewGuid().ToString() + ".csv")
It is highly improbable that the computer will generate the same Guid at the same instant of time. The only weakness i see here is the performance impact DateTime.Now.Ticks will add.
In my opinion, most answers proposed here as sub-optimal. The one coming closest is the original one proposed initially by Brann.
A Temp Filename must be
Unique
Conflict-free (not already exist)
Atomic (Creation of Name & File in the same operation)
Hard to guess
Because of these requirements, it is not a godd idea to program such a beast on your own. Smart People writing IO Libraries worry about things like locking (if needed) etc.
Therefore, I see no need to rewrite System.IO.Path.GetTempFileName().
This, even if it looks clumsy, should do the job:
//Note that this already *creates* the file
string filename1 = System.IO.Path.GetTempFileName()
// Rename and move
filename = filename.Replace(".tmp", ".csv");
File.Move(filename1 , filename);
I mixed #Maxence and #Mitch Wheat answers keeping in mind I want the semantic of GetTempFileName method (the fileName is the name of a new file created) adding the extension preferred.
string GetNewTempFile(string extension)
{
if (!extension.StartWith(".")) extension="." + extension;
string fileName;
bool bCollisions = false;
do {
fileName = Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString() + extension);
try
{
using (new FileStream(fileName, FileMode.CreateNew)) { }
bCollisions = false;
}
catch (IOException)
{
bCollisions = true;
}
}
while (bCollisions);
return fileName;
}
This could be handy for you... It's to create a temp. folder and return it as a string in VB.NET.
Easily convertible to C#:
Public Function GetTempDirectory() As String
Dim mpath As String
Do
mpath = System.IO.Path.Combine(System.IO.Path.GetTempPath, System.IO.Path.GetRandomFileName)
Loop While System.IO.Directory.Exists(mpath) Or System.IO.File.Exists(mpath)
System.IO.Directory.CreateDirectory(mpath)
Return mpath
End Function
This seems to work fine for me: it checks for file existance and creates the file to be sure it's a writable location.
Should work fine, you can change it to return directly the FileStream (which is normally what you need for a temp file):
private string GetTempFile(string fileExtension)
{
string temp = System.IO.Path.GetTempPath();
string res = string.Empty;
while (true) {
res = string.Format("{0}.{1}", Guid.NewGuid().ToString(), fileExtension);
res = System.IO.Path.Combine(temp, res);
if (!System.IO.File.Exists(res)) {
try {
System.IO.FileStream s = System.IO.File.Create(res);
s.Close();
break;
}
catch (Exception) {
}
}
}
return res;
} // GetTempFile
Based on answers I found from the internet, I come to my code as following:
public static string GetTemporaryFileName()
{
string tempFilePath = Path.Combine(Path.GetTempPath(), "SnapshotTemp");
Directory.Delete(tempFilePath, true);
Directory.CreateDirectory(tempFilePath);
return Path.Combine(tempFilePath, DateTime.Now.ToString("MMddHHmm") + "-" + Guid.NewGuid().ToString() + ".png");
}
And as C# Cookbook by Jay Hilyard, Stephen Teilhet pointed in Using a Temporary File in Your Application:
you should use a temporary file whenever you need to store
information temporarily for later retrieval.
The one thing you must remember is to delete this temporary file
before the application that created it is terminated.
If it is not deleted, it will remain in the user’s temporary
directory until the user manually deletes it.
This is what I am doing:
string tStamp = String.Format("{0:yyyyMMdd.HHmmss}", DateTime.Now);
string ProcID = Process.GetCurrentProcess().Id.ToString();
string tmpFolder = System.IO.Path.GetTempPath();
string outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";
This is a simple but effective way to generate incremental filenames. It will look in the current directly (you can easily point that somewhere else) and search for files with the base YourApplicationName*.txt (again you can easily change that). It will start at 0000 so that the first file name will be YourApplicationName0000.txt. if for some reason there are file names with junk between (meaning not numbers) the left and right parts, those files will be ignored by virtue of the tryparse call.
public static string CreateNewOutPutFile()
{
const string RemoveLeft = "YourApplicationName";
const string RemoveRight = ".txt";
const string searchString = RemoveLeft + "*" + RemoveRight;
const string numberSpecifier = "0000";
int maxTempNdx = -1;
string fileName;
string [] Files = Directory.GetFiles(Directory.GetCurrentDirectory(), searchString);
foreach( string file in Files)
{
fileName = Path.GetFileName(file);
string stripped = fileName.Remove(fileName.Length - RemoveRight.Length, RemoveRight.Length).Remove(0, RemoveLeft.Length);
if( int.TryParse(stripped,out int current) )
{
if (current > maxTempNdx)
maxTempNdx = current;
}
}
maxTempNdx++;
fileName = RemoveLeft + maxTempNdx.ToString(numberSpecifier) + RemoveRight;
File.CreateText(fileName); // optional
return fileName;
}
Easy Function in C#:
public static string GetTempFileName(string extension = "csv")
{
return Path.ChangeExtension(Path.GetTempFileName(), extension);
}
In this what we can do we can first find the extension of file
which is coming from file and after finding its extension.Then we
can create the temprary name of file and after that we can change
extension by the previous one it will works.
var name = Path.GetTempFileName();
var changename = Path.GetFileName(name);
var fileName = Path.ChangeExtension(changename, fileExtension);
I think you should try this:
string path = Path.GetRandomFileName();
path = Path.Combine(#"c:\temp", path);
path = Path.ChangeExtension(path, ".tmp");
File.Create(path);
It generates a unique filename and creates a file with that file name at a specified location.