C# using streamwriter and streamreader at the same time to change line - c#

so i searched a lot about how to change specific line in file, so i found the code which deletes line. I managed to edit the code and it changes exact line, but when i want to change other line after, previous word resets, and I dont know why.
This is the code which i found:
using (StreamReader reader = new StreamReader("C:\\input")) {
using (StreamWriter writer = new StreamWriter("C:\\output")) {
while ((line = reader.ReadLine()) != null) {
if (String.Compare(line, line_to_delete) == 0)
continue;
writer.WriteLine(line);
}
}
}
This is my edited code:
public void ChangeLineInFile(string file, string file2, string linetochange, string changedline) {
string line = null;
using (StreamReader reader = new StreamReader(file)) {
using (StreamWriter writer = new StreamWriter(file2)) {
while ((line = reader.ReadLine()) != null) {
if (String.Equals(line, linetochange)) {
writer.WriteLine(string.Format("{0}", changedline));
} else {
writer.WriteLine(line);
}
}
}
}
File.Delete(file);
File.Move(file2, file);
File.Delete(file2);
}

seems to me that your code does the following
file1->editA->file2
then you run it again. Did you copy file2 back over file1 before you started or did you just do
file1->editB->file2
After the first pass file1 still has the original data in it. So when you finish the function you need to copy the output file over the input file.

As I just explained above in the comments.
You are probably including your source file, the file you want to change, in Visual Studio in your project.
When you run your code from Visual Studio it will copy that source file to the output directory even if there is already a file present from the last execution.
The original file in your Visual Studio Project is not altered, only the copy that is included with the build output.
With each execution you are starting with the original file you have included in the project, you are not continuing with the file you had changed as this file is overwritten.
That said there are ways that you can specify if you want to include files as output during a build or if they should overwrite an existing file or not. Click on the file in the solution explorer and go to the properties (F4 shortcut on the keyboard). Both the Build Action and the Copy to Output Directory have to be taken into account.

Related

How to save a file that already exists?

I'm going to launch a code editor for people to create bots to disagree, it's almost all ready, but what I need help is when saving the file, I created a function that saves but when the file already exists the person have to replace, then I created a String called currentFile that will store the path of the selected file, then how do I make it just replace the text inside the file without needing to replace the file or open the save menu?
String currentFile = "C:\\Program Files (x86)\\EXAMPLE\\FILE.js";
SaveFileDialog sfd = default(SaveFileDialog);
if (fctb_code.Text.Length > 0)
{
sfd = new SaveFileDialog();
//sfd.Filter = "All Files|*.*";
//sfd.DefaultExt = "html";
sfd.ShowDialog();
string location = currentFile;
string sourcecode = fctb_code.Text;
location = sfd.FileName;
if (!object.ReferenceEquals(sfd.FileName, ""))
{
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(location, false))
{
writer.Write(sourcecode);
writer.Dispose();
}
}
And I want that when the file exists it just replaces the text inside the file, but when it doesn't exist it saves the file as a new one and opens SaveFileDialog.
All the code you have posted underneath sfd.ShowDialog(); can be replaced with one simple command (and an if statement)
if (sfd.FileName != "")
{
System.IO.File.WriteAllText(currentFile, fctb_code.Text);
}
No need for Streams and StreamWriters. No obtuse if logic.
To quote the documentation for File.WriteAllText, this will do overwriting for you:
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.

How to remove all lines in a file, then rewrite the file in Compact Framework 3.5 c#

In the .net framework using a Windows Forms app I can purge a file, then write the data that I want back to into that file.
Here is the code that I use in Windows Forms:
var openFile = File.OpenText(fullFileName);
var fileEmpty = openFile.ReadLine();
if (fileEmpty != null)
{
var lines = File.ReadAllLines(fullFileName).Skip(4); //Will skip the first 4 then rewrite the file
openFile.Close();//Close the reading of the file
File.WriteAllLines(fullFileName, lines); //Reopen the file to write the lines
openFile.Close();//Close the rewriting of the file
}
openFile.Close();
openFile.Dispose();
I am trying to do the same thing the compact framework. I can keep the lines that I want, and then delete all the lines in the file. However I am not able to rewrite the file.
Here is my compact framework code:
var sb = new StringBuilder();
using (var sr = new StreamReader(fullFileName))
{
// read the first 4 lines but do nothing with them; basically, skip them
for (int i = 0; i < 4; i++)
sr.ReadLine();
string line1;
while ((line1 = sr.ReadLine()) != null)
{
sb.AppendLine(line1);
}
}
string allines = sb.ToString();
openFile.Close();//Close the reading of the file
openFile.Dispose();
//Reopen the file to write the lines
var writer = new StreamWriter(fullFileName, false); //Don't append!
foreach (char line2 in allines)
{
writer.WriteLine(line2);
}
openFile.Close();//Close the rewriting of the file
}
openFile.Close();
openFile.Dispose();
Your code
foreach (char line2 in allines)
{
writer.WriteLine(line2);
}
is writing out the characters of the original file, each on a separate line.
Remember, allines is a single string that happens to have Environment.NewLine between the original strings of the file.
What you probably intend to do is simply
writer.WriteLine(allines);
UPDATE
You are closing openFile a number of times (you should only do this once), but you are not flushing or closing your writer.
Try
using (var writer = new StreamWriter(fullFileName, false)) //Don't append!
{
writer.WriteLine(allines);
}
to ensure the writer is disposed and therefore flushed.
If you plan to do this to have something like a "rotating" buffer for a log file consider that most Windows CE devices uses flash as storage media and your approach will generate a full re-write of the whole file (whole - 4 lines) every time. If this happens quite often (every few seconds) this may wear our the flash, reaching its maximum number of erase cycles quickly (quickly may mean a few weeks or months).
An alternative approach would be rename the old log file when it has reached the maximum size (deleting any existing file with the same name) and create a new one.
In this was you logging info would be split on two files but you'll always append to the existing files, limiting the number of writes you perform. Also renaming or deleting a file aren't heavy operations from the point of view of a flash file system.

Processing multiple drag and dropped files fails when using regular expressions

In my application the user can drag and drop multiple text files onto a GUI control to convert them to another format. Here is the relevant code:
private void panelConverter_DragDrop(object sender, DragEventArgs e)
{
string[] filenames = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string filename in filenames)
{
convertFile(filename);
}
}
private void convertFile(string filename)
{
// build name of output file
string convertedFile = Path.ChangeExtension(filename, ".out");
// open input file for reading
FileInfo source = new FileInfo(filename);
StreamReader srcStream = source.OpenText();
// open output file for writing
StreamWriter dstStream = new StreamWriter(convertedFile);
// loop over input file
string line;
do
{
// get next line from input file
line = srcStream.ReadLine();
if (!Regex.IsMatch(line, #"fred=\d+"))
{
dstStream.WriteLine(line);
dstStream.Flush();
}
} while (line != null);
}
The problem is that when I drop multiple files on the GUI, only one of them actually gets processed. I have found that if I comment out the Regex line, all of the dropped files are processed. Am I missing something in my handling of regular expressions in this context?
Try following variation of the method:
private void convertFile(string filename)
{
// build name of output file
string convertedFile = Path.ChangeExtension(filename, ".out");
// open input file for reading
FileInfo source = new FileInfo(filename);
StreamReader srcStream = source.OpenText();
// open output file for writing
using (StreamWriter dstStream = File.CreateText(convertedFile))
{
// loop over input file
string line;
do
{
// get next line from input file
line = srcStream.ReadLine();
if (!Regex.IsMatch(line, #"fred=\d+"))
{
dstStream.WriteLine(line);
dstStream.Flush();
}
} while (line != null);
}
Debug.WriteLine(string.Format("File written to: {0}", convertedFile));
}
The main modification is use of using keyword which would guarantee disposal and closing of the file resource. If problem is not still resolved then try followings:
Do you have any global exception handlers? Make sure you check Debug > Exceptions... so that Visual Studio automatically breaks on the line where exception is thrown. See this article on how-to.
Make sure files are written at correct places. If files have full path then the Debug.WriteLine statement above would tell you were the files are being written.
You should get at least 0 length file written on the disk if no exceptions are occurring.

C# Streamreader writer (memory issues)

I have a few multimillion lined text files located in a directory, I want to read line by line and replace “|” with “\” and then write out the line to a new file. This code might work just fine but I’m not seeing any resulting text file, or it might be I’m just be impatient.
{
string startingdir = #"K:\qload";
string dest = #"K:\D\ho\jlg\load\dest";
string[] files = Directory.GetFiles(startingdir, "*.txt");
foreach (string file in files)
{
StringBuilder sb = new StringBuilder();
using (FileStream fs = new FileStream(file, FileMode.Open))
using (StreamReader rdr = new StreamReader(fs))
{
while (!rdr.EndOfStream)
{
string begdocfile = rdr.ReadLine();
string replacementwork = docfile.Replace("|", "\\");
sb.AppendLine(replacementwork);
FileInfo file_info = new FileInfo(file);
string outputfilename = file_info.Name;
using (FileStream fs2 = new FileStream(dest + outputfilename, FileMode.Append))
using (StreamWriter writer = new StreamWriter(fs2))
{
writer.WriteLine(replacementwork);
}
}
}
}
}
DUHHHHH Thanks to everyone.
Id10t error.
Get rid of the StringBuilder, and do not reopen the output file for each line:
string startingdir = #"K:\qload";
string dest = #"K:\D\ho\jlg\load\dest";
string[] files = Directory.GetFiles(startingdir, "*.txt");
foreach (string file in files)
{
var outfile = Path.Combine(dest, Path.GetFileName(file));
using (StreamReader reader = new StreamReader(file))
using (StreamWriter writer = new StreamWriter(outfile))
{
string line = reader.ReadLine();
while (line != null)
{
writer.WriteLine(line.Replace("|", "\\"));
line = reader.ReadLine();
}
}
}
Why are you using a StringBuilder - you are just filling up your memory without doing anything with it.
You should also move the FileStream and StreamWriter using statements to outside of your loop - you are re-creating your output streams for every line, causing unneeded IO in the form of opening and closing the file.
Use Path.Combine(dest, outputfilename), from your code it looks like you're writing to the file K:\D\ho\jlg\load\destouputfilename.txt
This code might work just fine but I’m not seeing any resulting text file, or it might be I’m just be impatient.
Have you considered having a Console.WriteLine in there to check the progress. Sure, it's going to slow down performance a tiny tiny bit - but you'll know what's going on.
It looks like you might want to do a Path.Combine, so that instead of new FileStream(dest + outputfilename), you have new FileStream(Path.Combine(dest + outputfilename)), which will create the files in the directory that you expect, rather than creating them in K:\D\ho\jlg\load.
However, I'm not sure why you're writing to a StringBuilder that you're not using, or why you're opening and closing the file stream and stream writer on each line that you're writing, is that to force the writer to flush it's output? If so, it might be easier to just flush the writer/stream on each write.
you're opening and closing the output strean for each line in the output, you'll have to be very patient!
open it once outside the loop.
I guess the problem is here:
string begdocfile = rdr.ReadLine();
string replacementwork = docfile.Replace("|", "\\");
you're reading into begdocfile variable but replacing chars in docfile which I guess is empty
string replacementwork = docfile.Replace("|", "\\");
I believe the above line in your code is incorrect : it should be "begdocfile.Replace ..." ?
I suggest you focus on getting as much of the declaration and "name manufacture" out of the inner loop as possible : right now you are creating new FileInfo objects, and path names for every single line you read in every file : that's got to be hugely expensive.
make a single pass over the list of target files first, and create, at one time, the destination files, perhaps store them in a List for easy access, later. Or a Dictionary where "string" will be the new file path associated with that FileInfo ? Another strategy : just copy the whole directory once, and then operate to directly change the copied files : then rename them, rename the directory, whatever.
move every variable declaration out of that inner loop, and within the using code blocks you can.
I suspect you are going to hear from someone here at more of a "guru level" shortly who might suggest a different strategy based on a more profound knowledge of streams than I have, but that's a guess.
Good luck !

Why can't this file be deleted after using C1ZipFile?

The following code gives me a System.IO.IOException with the message 'The process cannot access the file'.
private void UnPackLegacyStats()
{
DirectoryInfo oDirectory;
XmlDocument oStatsXml;
//Get the directory
oDirectory = new DirectoryInfo(msLegacyStatZipsPath);
//Check if the directory exists
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oFile.FullName))
{
//Open the zip file
using (C1ZipFile oZipFile = new C1ZipFile(oFile.FullName, false))
{
//Check if the zip contains the stats
if (oZipFile.Entries.Contains("Stats.xml"))
{
//Get the stats as a stream
using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
{
//Load the stats as xml
oStatsXml = new XmlDocument();
oStatsXml.Load(oStatsStream);
//Close the stream
oStatsStream.Close();
}
//Loop hit elements
foreach (XmlElement oHitElement in oStatsXml.SelectNodes("/*/hits"))
{
//Do stuff
}
}
//Close the file
oZipFile.Close();
}
}
//Delete the file
oFile.Delete();
}
}
}
I am struggling to see where the file could still be locked. All objects that could be holding onto a handle to the file are in using blocks and are explicitly closed.
Is it something to do with using FileInfo objects rather than the strings returned by the static GetFiles method?
Any ideas?
I do not see problems in your code, everything look ok. To check is the problem lies in C1ZipFile I suggest you initialize zip from stream, instead of initialization from file, so you close stream explicitly:
//Open the zip file
using (Stream ZipStream = oFile.OpenRead())
using (C1ZipFile oZipFile = new C1ZipFile(ZipStream, false))
{
// ...
Several other suggestions:
You do not need to call Close() method, with using (...), remove them.
Move xml processing (Loop hit elements) outsize zip processing, i.e. after zip file closeing, so you keep file opened as least as possible.
I assume you're getting the error on the oFile.Delete call. I was able to reproduce this error. Interestingly, the error only occurs when the file is not a zip file. Is this the behavior you are seeing?
It appears that the C1ZipFile.IsZipFile call is not releasing the file when it's not a zip file. I was able to avoid this problem by using a FileStream instead of passing the file path as a string (the IsZipFile function accepts either).
So the following modification to your code seems to work:
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
using (FileStream oStream = new FileStream(oFile.FullName, FileMode.Open))
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oStream))
{
// ...
}
}
//Delete the file
oFile.Delete();
}
}
In response to the original question in the subject: I don't know if it's possible to know if a file can be deleted without attempting to delete it. You could always write a function that attempts to delete the file and catches the error if it can't and then returns a boolean indicating whether the delete was successful.
I'm just guessing: are you sure that oZipFile.Close() is enough? Perhaps you have to call oZipFile.Dispose() or oZipFile.Finalize() to be sure it has actually released the resources.
More then Likely it's not being disposed, anytime you access something outside of managed code(streams, files, etc.) you MUST dispose of them. I learned the hard way with Asp.NET and Image files, it will fill up your memory, crash your server, etc.
In the interest of completeness I am posing my working code as the changes came from more than one source.
private void UnPackLegacyStats()
{
DirectoryInfo oDirectory;
XmlDocument oStatsXml;
//Get the directory
oDirectory = new DirectoryInfo(msLegacyStatZipsPath);
//Check if the directory exists
if (oDirectory.Exists)
{
//Loop files
foreach (FileInfo oFile in oDirectory.GetFiles())
{
//Set empty xml
oStatsXml = null;
//Load file into a stream
using (Stream oFileStream = oFile.OpenRead())
{
//Check if file is a zip file
if (C1ZipFile.IsZipFile(oFileStream))
{
//Open the zip file
using (C1ZipFile oZipFile = new C1ZipFile(oFileStream, false))
{
//Check if the zip contains the stats
if (oZipFile.Entries.Contains("Stats.xml"))
{
//Get the stats as a stream
using (Stream oStatsStream = oZipFile.Entries["Stats.xml"].OpenReader())
{
//Load the stats as xml
oStatsXml = new XmlDocument();
oStatsXml.Load(oStatsStream);
}
}
}
}
}
//Check if we have stats
if (oStatsXml != null)
{
//Process XML here
}
//Delete the file
oFile.Delete();
}
}
}
The main lesson I learned from this is to manage file access in one place in the calling code rather than letting other components manage their own file access. This is most apropriate when you want to use the file again after the other component has finished it's task.
Although this takes a little more code you can clearly see where the stream is disposed (at the end of the using), compared to having to trust that a component has correctly disposed of the stream.

Categories

Resources