c# compare 2 files - create and update functions - c#

It is my first time to write code to function file operations. I need to compare old file with a new file. If the old file name is equal to a new file name, it needs to be overwritten (update). If not equal, creating a new file name. How to do this in a simple and best way?
public void FileCreateOrOverwritten(string file)
{
try
{
if (File.Exists(file))
{
if (file == newFile)
{
//how to replace old file with a new one with new data (xml document)
//need to use filestream
}
else
{
//how to create a new file with new data (xml document)
}
}
.
.
.
}

To (over)write a file,
using (var writer = File.CreateText(file))
{
for (...)
{
writer.WriteLine(...);
}
}
You don't then need to decide if you have an old one to over-write or a new one to create.
From the docs
"This method is equivalent to the StreamWriter(String, Boolean)
constructor overload with the append parameter set to false. If the
file specified by path does not exist, it is created. If the file does
exist, its contents are overwritten"
If you are new to this, note the using

Related

c# File.WriteAllText overwriting new text on old Text

While inserting new text into Json file its overwriting new text (insted of writing in new line).
Here is my code
public static void WriteToJson()
{
string filepath = #"../../SchemaList.json";
// string filepath = #"C:\HoX_code\learning\Readjson\Readjson\SchemaList.json";
List<SchemaInfo> _oscheme = new List<SchemaInfo>();
_oscheme.Add(new SchemaInfo()
{
AuthenticateCmdlets= "AuthenticateCmdlets1",
GetPowerState= "GetPowerState1",
PowerOff= "PowerOff",
PowerOn= "PowerOn",
});
string json = JsonConvert.SerializeObject(_oscheme.ToArray(), Formatting.Indented);
using (StreamWriter sw = new StreamWriter(#"C:\HoX_code\learning\Readjson\Readjson\SchemaList.json"))
{
sw.Write(json);
}
/// File.WriteAllText(#"../../SchemaList.json", json);
}
Here i use File.WriteAllText & StreamWriter Both are working same
This is intended behaivour. Checking the documentation for File.WriteAllText it says
Creates a new file, write the contents to the file, and then closes the file. If the target file already exists, it is overwritten.
(Emphasis mine)
You're going to want to use File.AppendAllText, it's documentation states
Appends the specified string to the file, creating the file if it does not already exist.
If you want to use streams, then the StreamWriter constructor has a parameter to specify whether or not to append or overwrite text in a file here (Credits go to #vernou, I totally forgot this constructor existed)
But do note that this will result in invalid JSON, as JSON may only have 1 base object and this will result in multiple. If you actually want to append to the serialized JSON object, the I recommend you check out How to Append a json file without disturbing the formatting

XML file from ZIP Archive is incomplete in C#

I've work with large XML Files (~1000000 lines, 34mb) that are stored in a ZIP archive. The XML file is used at runtime to store and load app settings and measurements. The gets loadeted with this function:
public static void LoadFile(string path, string name)
{
using (var file = File.OpenRead(path))
{
using (var zip = new ZipArchive(file, ZipArchiveMode.Read))
{
var foundConfigurationFile = zip.Entries.First(x => x.FullName == ConfigurationFileName);
using (var stream = new StreamReader(foundConfigurationFile.Open()))
{
var xmlSerializer = new XmlSerializer(typeof(ProjectConfiguration));
var newObject = xmlSerializer.Deserialize(stream);
CurrentConfiguration = null;
CurrentConfiguration = newObject as ProjectConfiguration;
AddRecentFiles(name, path);
}
}
}
}
This works for most of the time.
However, some files don't get read to the end and i get an error that the file contains non valid XML. I used
foundConfigurationFile.ExtractToFile();
and fount that the readed file stops at line ~800000. But this only happens inside this code. When i open the file via editor everything is there.
It looks like the zip doesnt get loaded correctly, or for that matter, completly.
Am i running in some limitations? Or is there an error in my code i don't find?
The file is saved via:
using (var file = File.OpenWrite(Path.Combine(dirInfo.ToString(), fileName.ToString()) + ".pwe"))
{
var zip = new ZipArchive(file, ZipArchiveMode.Create);
var configurationEntry = zip.CreateEntry(ConfigurationFileName, CompressionLevel.Optimal);
var stream = configurationEntry.Open();
var xmlSerializer = new XmlSerializer(typeof(ProjectConfiguration));
xmlSerializer.Serialize(stream, CurrentConfiguration);
stream.Close();
zip.Dispose();
}
Update:
The problem was the File.OpenWrite() method.
If you try to override a file with this method it will result in a mix between the old file and the new file, if the new file is shorter than the old file.
File.OpenWrite() doenst truncate the old file first as stated in the docs
In order to do it correctly it was neccesary to use the File.Create() method. Because this method truncates the old file first.

Problem in overwriting Application.persistentDataPath

I have a JSON file which contains data for my Localization in my game. The file is stored in Application.persistentDataPath . I read in the documentation that the file is not deleted or overwritten when updating the game. Currently I launch the app and the file is there but when I add more content and send an update to save again, it only has the original content.
But is there a way to update this file when I update the game?
What if in the future I want to add more content to this JSON file, I would have to delete it and upload it again?
Any way to update and overwrite it?
private static void Request(string file)
{
var loadingRequest = UnityWebRequest.Get(Path.Combine(Application.streamingAssetsPath, file));
loadingRequest.SendWebRequest();
while (!loadingRequest.isDone)
{
if (loadingRequest.isNetworkError || loadingRequest.isHttpError)
{
break;
}
}
if (loadingRequest.isNetworkError || loadingRequest.isHttpError)
{
// Some error happened.
}
else
{
File.WriteAllBytes(Path.Combine(Application.persistentDataPath, file), loadingRequest.downloadHandler.data);
}
}
// Loads key from "StreamingAssets/language_data.json" file.
public static string LoadJson(string key)
{
Request("data.json");
var jsonPath = Path.Combine(Application.persistentDataPath, jsonFileName);
using (StreamReader r = new StreamReader(jsonPath))
{
var N = JSON.Parse(r.ReadToEnd());
string result = N[GetLanguage()][key];
return result;
}
}
I think the issue is the following:
You are only creating a file named data.json NOT languages_data.json!
In Request (you pass in "data.json") you copy
Application.streamingAssetsPath, file
to
Application.persistentDataPath, file
where file = "data.json"
Then in LoadJson you are trying to load values from
Application.persistentDataPath, jsonFileName
where - as to your comments - jsonFileName = "languages_data.json"
=> It is a different path!
In the Editor/on your PC you probably still have an old languages_data.json in the persistent data from some time ago. Therefore there is no error but it is also never updated. Delete the persistent/languages_data.json on your PC and it will throw a FileNotFoundException.
To solve this make 100% sure the file names are the same everywhere. The simplest fix code wise would be to simply rather use
Request(jsonFileName);
and make sure that jsonFileName matches the name of your existing file in StreamingAssets.

IO Exception - File being used by another process (Can't open file following directory creation)

I have got a project on the go that monitors patients for a vet while they are being operated on and writes the result to a text file. While I was experimenting with the outputting I just let the files save in the Debug folder, which worked fine. However, I've now created a full directory that creates or opens a main folder, and then a sub folder (based on input text from the program), to save the text file into.
private void createDirectory()
{ //create output file in this folder using owner name and current date
//main folder path (contains all files output from system)
string rootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Horse Monitoring Records";
//sub folder path (each patient has individual subfolder)
string subDirectory = rootDirectory + "\\" + txtPatName.Text + "-" + txtOwnerName.Text;
//file name (patient has file created for each operation)
fileName = subDirectory + "\\" + txtOwnerName.Text + "-" + DateTime.Now.Date.ToString("ddMMyyyy") + ".txt";
if (!Directory.Exists(rootDirectory)) //if main folder does not exist...
{
Directory.CreateDirectory(rootDirectory); //create it in My Documents
}
if (!Directory.Exists(subDirectory)) //if patient sub folder does not exist...
{
Directory.CreateDirectory(subDirectory); //create it in Patient-Owner format
}
if (!File.Exists(fileName)) //if monitoring file does not exist...
{
File.Create(fileName); //create it in Owner-Date format
}
}
This stage works fine, but as soon as you try to save some data to the text file, it throws to a run time error stating that
The file cannot be accessed because it is being used by another process.
The exception is brought up here:
private void saveFileDetails()
{
//Once case details have been entered, create new file using these details and add data input structure
StreamWriter consoleFile = new StreamWriter(fileName);
...
}
When I went and checked out the folder, the relevant sub-folder and file had been created but the text file was blank.
I'm guessing it's something to do with closing the text file after creating the directory, which means it's already open when the system tries to open it. I can't figure out how to sort this issue out though!
The two functions shown above are called like this:
private void btnStart_Click(object sender, EventArgs e)
{
...
//file details entered upon load written to new file - according to PatID
createDirectory();
saveFileDetails();
}
Any suggestions on where to go from here would be very much appreciated!
Thanks,
Mark
The issue here is that you do
if (!File.Exists(fileName)) //if monitoring file does not exist...
{
File.Create(fileName); //create it in Owner-Date format
}
Right before you try to write to the file. Because you've just created it (if it didn't exist), chances are that the operating system hasn't released the file yet.
Like #Jauch mentioned in the comments, you could skip this check completely and use the StreamWriter overload which will create file if it doesn't exist, or append to it if it does.
private void saveFileDetails()
{
//Once case details have been entered, create new file using these details and add data input structure
using (StreamWriter consoleFile = new StreamWriter(fileName, true))
{
// ...
}
}
Alternatively you can use the following to write all of your text at once:
File.AppendAllText(textToWrite, fileName);
File.Create(fileName) returns an open stream to the file which is never closed.
To create an empty file use File.WriteAllBytes(fileName, new byte[0]);
Otherwise the 2 methods can be shortend
private void SaveFileDetails()
{
string subDirectory = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"Horse Monitoring Records");
// create the folder hierarchy if not exists. does nothing if already there
Directory.CreateDirectory(subDirectory);
// each patient has individual file
var filepath = Path.Combine(subDirectory,
txtPatName.Text + "-" + txtOwnerName.Text + "-" + DateTime.Now.Date.ToString("yyyyMMdd") + ".txt");
// creates the file if not exists
using (var writer = new StreamWriter(filepath, append: true, encoding: Encoding.UTF8))
{
// write details
}
}
Note:
merged 2 methods
.NET naming conventions applied
changed dateformat to better sort by name in explorer
StreamWriter implements IDisposable, so wrapping it in a using block can manage closing and disposing the writer and ensuring it is available the next time you want to touch that file. It can also manage creating the text file if it doesn't exist, removing the need to explicitly call File.Create.
StreamWriter consoleFile = new StreamWriter(fileName);
becomes
using (StreamWriter writer = File.AppendText("log.txt"))
{
// writing, etc.
}
or
using (StreamWriter writer = new StreamWriter(fileName, true))
{ // true says "append to file if it exists, create if it doesn't"
// writing, etc.
}
Whatever seems more readable to you will work fine.

Search and replace contents of text file based on mappings in CSV file in C#

I am new to programming and am working on a C# project that will search and replace certain words in a text file with new values. I have some code that works, but the OLD and NEW values are hardcoded right now. I would like to use an external CSV file as a configuration file so the user can add or update the OLD to NEW mappings at a later time. This is my current code with the OLD and NEW values hardcoded:
try
{
StreamReader file = new StreamReader(inputfullfilepath);
TextWriter writer = new StreamWriter(outputfile);
while ((line = file.ReadLine()) != null)
{
line = line.Replace("OLD1", "NEW1");
line = line.Replace("OLD2", "NEW2");
// etc....
writer.WriteLine(line);
}
file.Close();
File.Move(inputfullfilepath, inputfullfilepath + ".old");
writer.Close();
File.Move(outputfile, outputfilepath + #"\" + inputfilename);
MessageBox.Show("File Scrub Complete", "Success");
}
catch
{
MessageBox.Show("Error: Be sure data paths are valid.", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
The code takes each line of the text file, tries to do a search/replace for all OLD to NEW mappings, then goes to the next line in the text file. The problem I am trying to wrap my head around is being able to make this list of OLD to NEW mappings dynamic based on a CSV (or XML if that would be easier?) configuration file so the user can add new search/replace keywords.
I tried to use the C# Application Settings in Visual Studio (which creates an XML configuration file) but I had a really hard time understanding how to make that work. What's the best way to do this so the values don't have to be hardcoded?
A csv file will work just fine.
I'll create a new Object which i'll call ReplaceObject
public ReplaceObject()
{
public string original;
public string updated;
//ideally you'd use getters and setters, but I'll keep it simple
}
Now we read from the csv
List<ReplaceObject> replaceList = new List<ReplaceObject>
while (reader.peek != -1)
{
string line = reader.readln();
var splitLine = line.split(',');
ReplaceObject rObject = new ReplaceObject();
rObject.original = splitLine[0];
rObject.updated = splitLine[1];
replaceList.add(rObject);
}
Now we go through the list.. and replace
string entireFile = //read all of it
foreach (ReplaceObject o in replaceList)
{
entireFile.Replace(o.original,o.updated);
}
//write it at the end
(Note that my code is missing some checks, but you should get the idea. Also you might want to use a StringBuilder)
My suggestion would be that you use the Settings.cs instead of CSV
It is very easy to use them and involves very less code
e.g. Properties.Settings.Default.Old1;
Here is a walkthrough http://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
See this example showing how you can use it http://www.codeproject.com/Articles/17659/How-To-Use-the-Settings-Class-in-C

Categories