I'm writing text to files using StreamWriter using the following code:
path == #"Desktop\";
filepath1 = path + "1.txt";
StreamWriter _sw = new StreamWriter(filepath1, true);
_sw.WriteLine("some Text");
_sw.Close();
if size of textfile exceeds 500kb I want to create text files dynamically. I'm tryng following code:
var size = (path.Length)/1024;
if(size>=500)
{
int i = (size/500)+1;
var filepath2 = path + i + ".txt";
if (File.Exists(filepath2))
{
StreamWriter _sw = new StreamWriter(filepath2, true);
_sw.WriteLine("Some message");
_sw.Close();
}
}
else
{
FileStream fs = File.Create(filepath2);
StreamWriter _sw = new StreamWriter(filepath2, true);
_sw.WriteLine(ex);
_sw.Close();
}
My question is if file 2.txt also exceeds 500kb I want to create 3.txt,4.txt..... and so on..
I want to create all these dynamically - how to solve this problem?
First thing you need to do the SIZE comparison for the data length of File not the File Path.
Here is Function which dose what you want to achieve, Please make appropriate changes for your path.
//Public variable to manage file names
int FileCounter = 1;
string FileName;
// Call this function to Add text to file
private void WriteToFile(string writeText)
{
FileName = "MyFile_"+FileCounter +".txt";
if (File.Exists(FileName))
{
string str = File.ReadAllText(FileName);
if ((str.Length + writeText.Length) / 1024 > 500) // check for limit
{
// Create new File
FileCounter++;
FileName = "MyFile_" + FileCounter + ".txt";
StreamWriter _sw = new StreamWriter(FileName, true);
_sw.WriteLine(writeText);
_sw.Close();
}
else // use exixting file
{
StreamWriter _sw = new StreamWriter(FileName, true);
_sw.WriteLine(writeText);
_sw.Close();
}
}
}
Where to start..
You are writing it as one big long procedural script. You need to break it down into chunks that can be reused using functions. As it is, it will get out of control way too quickly.
path == #"Desktop\"; is not valid. 1 too many =
Use Path.Combine() to combine your folder and filenames.
I'm sure this is all just test/rough/scratch code but just in case it's not, also check out Try/Except to wrap your file handling. You should also look up using() to dispose of your streams/writers.
My last comment would be that I see a lot of this sort of code a lot and it's often replaceable with something like Nlog for a whole lot less friction.
I would have commented but this login has no rep.
Related
I have a Windows forms application, that is supposed to store its config data on a file called config.txt. I have this code, which runs when the form is initialized. Here is the function:
string folderPath = #‘C:\Users\Public\Periodic_Clock\’;
string filePath = #‘C:\Users\Public\Periodic_Clock\config.txt’;
public Form1()
{
InitializeComponent();
once = 0;
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
if (!File.Exists(filePath))
{
new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite);
File.WriteAllText(filePath, "Alarm" + Environment.NewLine + "Alarm Ringing!" + Environment.NewLine + 1);
}
else
{
}
notificationTitle = File.ReadLines(filePath).Skip(0).Take(1).First();
notificationText = File.ReadLines(filePath).Skip(1).Take(1).First();
}
Pretty much, I have an app that sends a notification when a timer goes off. I want to use this config file to store a custom title, text and notification type for the notification. (so that the user can customize what the notification looks like) My intention with this was to have the first line as the notification title, second line as the text, and the third line as a number from 0 - 3, to store the type of notification (info, error, etc.)
I can not manage to get the program to write anything to the text file. It creates it, but it seems to completely skip the part where I use File.WriteAllText(). I used other methods like StreamWriter, but nothing was written to the text document. The code right now skips the WriteAllText part and creates an error at File.ReadLines, which just tells me that the sequence contains no elements. What's the problem here? How do I fix it?
In the end, I ended up with this code:
public Form1()
{
InitializeComponent();
once = 0;
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
try
{
FileStream config = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
config.Close();
StreamWriter writetext = new StreamWriter(filePath);
writetext.Write("Alarm");
writetext.WriteLine();
writetext.Write("Alarm Ringing!");
writetext.WriteLine();
writetext.Write("1");
writetext.Close();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Error!");
}
notificationTitle = File.ReadLines(filePath).Skip(0).Take(1).First();
notificationText = File.ReadLines(filePath).Skip(1).Take(1).First();
}
I am trying to Write to a text file after this code block checks for the last time the PC was restarted. The code below reads from a text file, the last time the PC was resarted, and from there it determines whether to show a splash-screen. However, After this method runs, i need to write to the text file what the current "System Up-Time" is. But i keep getting an error that says the text file is in use. This has driven me insane. I have made sure all StreamWriters and StreamReaders are closed. I have tried Using Statements. I have tried GC.Collect. I feel like i have tried everything.
Any help would be appreciated.
private void checkLastResart()
{
StreamReader sr = new StreamReader(Path.GetDirectoryName(Application.ExecutablePath) + #"\Settings.txt");
if (sr.ReadLine() == null)
{
sr.Close();
MessageBox.Show("There was an error loading 'System UpTime'. All settings have been restored to default.");
StreamWriter sw = new StreamWriter(Path.GetDirectoryName(Application.ExecutablePath) + #"\Settings.txt", false);
sw.WriteLine("Conversion Complete Checkbox: 0");
sw.WriteLine("Default Tool: 0");
sw.WriteLine("TimeSinceResart: 0");
sw.Flush();
sw.Close();
}
else
{
try
{
StreamReader sr2 = new StreamReader(Path.GetDirectoryName(Application.ExecutablePath) + #"\Settings.txt");
while (!sr2.EndOfStream)
{
string strSetting = sr2.ReadLine();
if (strSetting.Contains("TimeSinceResart:"))
{
double lastTimeRecorded = double.Parse(strSetting.Substring(17));
//If the lastTimeRecorded is greater than timeSinceResart (computer has been resarted) OR 2 hours have passed since LVT was last run
if (lastTimeRecorded > timeSinceRestart || lastTimeRecorded + 7200 < timeSinceRestart)
{
runSplashScreen = true;
}
else
{
runSplashScreen = false;
}
}
}
sr2.Close();
sr2.Dispose();
}
catch (Exception e) { MessageBox.Show("An error has occured loading 'System UpTime'.\r\n\r\n" + e); }
}
}
Below is a sample of writing to the Text file, after the above code has been run. It doesnt matter if i open a StreamWriter, or use File.WriteAllLines, an error is thrown immediately.
StreamWriter sw = new StreamWriter(Path.GetDirectoryName(Application.ExecutablePath) + #"\Settings.txt");
string[] lines = File.ReadAllLines(Path.GetDirectoryName(Application.ExecutablePath) + #"\Settings.txt");
lines[2] = "TimeSinceResart: " + timeSinceRestart;
foreach (string s in lines)
sw.WriteLine(s);
Your writing code should be changed in this way
string file = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath),"Settings.txt");
// First read the two lines in memory
string[] lines = File.ReadAllLines(file);
// then use the StreamWriter that locks the file
using(StreamWriter sw = new StreamWriter(file))
{
lines[2] = "TimeSinceResart: " + timeSinceRestart;
foreach (string s in lines)
sw.WriteLine(s);
}
In this way the lock on the StreamWriter doesn't block the reading with FileReadAllLines.
Said that, please note a couple of things. Do not create path strings with string concatenation, use the static methods of the Path class. But most important, when you create a disposable object like a stream be sure to use the using statement to close correctly the file
To complete the answer in response to your comment. Using statement also for the first part of your code
private void checkLastResart()
{
string file = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath),"Settings.txt");
using(StreamReader sr = new StreamReader(file))
{
if (sr.ReadLine() == null)
{
sr.Close();
MessageBox.Show(...)
using(StreamWriter sw = new StreamWriter(file, false))
{
sw.WriteLine("Conversion Complete Checkbox: 0");
sw.WriteLine("Default Tool: 0");
sw.WriteLine("TimeSinceResart: 0");
sw.Flush();
}
}
else
{
....
}
} // exit using block closes and disposes the stream
}
Where you create sr2, sr still has settings.txt open.
I have txt file(65mb)i need to read line by line and change each line,
For example i have many lines
User=value Password=value Phone=123456789
User=value Password=value Phone=123456789
User=value Password=value Phone=123456789
and i need to change first number of credit card/Phone to*(security reason), and get text like this and save it, or just to change origin text file.
User=value Password=value Phone=*****6789
User=value Password=value Phone=*****6789
User=value Password=value Phone=*****6789
I build new string and add to there line(changed) by line than save, but it take me to many time this is my code
string NewPath = "";
string lineOfText;
string NewTextFile = "";
using (var filestream = new FileStream(FilePath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
{
var file = new StreamReader(filestream, Encoding.UTF8, true, 128);
while ((lineOfText = file.ReadLine()) != null)//here i reading line by line
{
NewTextFile += lineOfText.Substring(0, 124) + "************" +
lineOfText.Substring(136, lineOfText.Length - 136);
NewTextFile += Environment.NewLine;//here i make new string
}
}
NewPath = FilePatharr[1] + "\\temp.txt";
System.IO.File.WriteAllText(NewPath, NewTextFile);//here i save him
Do any one know better way to do this,my code is taking to long to save this big file.
UPDATE
Why do i get -2 for this question? Whats wrong with this question? I see here only wrong answers about how to pass sensitive data and more things that not belong to this questions When the question was -->Fast way to change txt file and save it
Any way i find out how to do this speed of saving file speedUp from 100kb\sec to 3MB\sec now it taking me 20sec and not 20min like before
Your primary problem here is that you're appending to a string. And that gets expensive very quickly. You should be able to process that 65 MB in about five seconds. Here's what I would do:
string outputFileName = "temp.txt";
using (var outputFile = new StreamWriter(outputFileName))
{
foreach (var line in File.ReadLines(inputFileName))
{
var newLine = line.Substring(0, 124) + "************" +
line.Substring(136, lineOfText.Length - 136);
outputFile.WriteLine(newLine);
}
}
This is going to be a lot faster than appending strings. If you really want to do it all in memory, then use a StringBuilder. Instead of
string NewTextFile = "";
Use
StringBuilder NewTextFile = new StringBuilder();
And when you're composing the output, replace the string concatenation with:
NewTextFile.AppendLine(
lineOfText.Substring(0, 124) + "************" +
lineOfText.Substring(136, lineOfText.Length - 136));
Finally, to write it to the file:
System.IO.File.WriteAllText(NewPath, NewTextFile.ToString());
You could keep WriteAllLines and use ReadAllLines:
ReadAllLines to get all lines as an array of strings
Enumerate the array and use string.Replace to replace its content, line by line
WriteAllLines and overwrite existing file
Or you could use stream reader/writer and read/write line by line. You currently are building a string with millions of concatenations, that's the performance issue.
That being said, you probably have an obvious security issue here. Why was critical information such as credit card numbers stored as plain text in the first place?
public static string ChangeCardNumber(string FilePath, string txtFileName)
{
string NewPath = "";
string lineOfText;
string NewTextFile = "";
NewPath = FilePatharr[0] + "\\temp_" + txtFileName;
using (var filestream = new FileStream(FilePath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
{
var file = new StreamReader(filestream, Encoding.UTF8, true, 128);
System.IO.File.Create(NewPath).Close();
int res = 0;
while ((lineOfText = file.ReadLine()) != null)
{
res++;
if (!lineOfText.Contains("EOF"))
{
NewTextFile += lineOfText.Substring(0, 124) + "************" +
lineOfText.Substring(136, lineOfText.Length - 136);
NewTextFile += Environment.NewLine;
}
if (res % 200 == 0 || lineOfText.Contains("EOF"))//check if endline and if in NewTextFile 200lines
{
System.IO.File.AppendAllText(NewPath, NewTextFile);
NewTextFile = "";
}
}
}
return NewPath;
}
Well I Guess You Could Do It Another Way And This Way Is The Way I Use For All of my saving needs.
if you want to save it all at the same time then this would work! or you could have it loaded in VARIABLES and then save it but this is the code to save it. it is very simple and easy to edit
There You Go
Basic Code To Add A New Line With Text
echo your text here >>filename.txt
For Explaination Please Visit The DropBox Link: https://www.dropbox.com/s/nufkecx1b8088cu/HOW%20TO%20USE%20THIS.txt
I am working on C# on Win7.
I need to use Streamwriter to write to a txt file.
StreamWriter outfile = new StreamWriter(MY_PATH, true);
foreach(a line of strings)
{
// process the line
outfile.Write(String.Format(WIDTH + " " + WIDTH, num1Str+"\t", num2Str+"\t"));
}
if all elements in line are "0"
// do not write anything to the file, clear outfile buffer
// WIDTH are constants. num1Str and num2Str are variables.
How to clear the contents written in the stream buffer ?
Flush is not a solution because I do not want to write the file if all elements are 0.
Any help would be appreciated.
I believe you're looking for outfile.Flush();.
UPDATE: so now that the question is clearer, you don't want a StreamWriter, you want to leverage something like a MemoryStream instead. Consider the following snippet:
var writeToDisk = false;
var outfile = new MemoryStream();
foreach(a line of strings)
{
// process the line
// BTW: the `String.Format` you have here is exceptionally confusing
// and may be attributing to why everything is \0
outfile.Write(...);
// set the flag to `true` on some condition to let yourself know
// you DO want to write
if (someCondition) { writeToDisk = true; }
}
if (writeToDisk)
{
var bytes = new byte[outfile.Length];
outfile.Read(bytes, 0, outfile.Length);
File.WriteAllBytes(MY_PATH, bytes);
}
I think what you want is the Any for checking if any is not "0", but also using using would be nice so that you can dispose properly.
if(someString.Any(a=> a != '0')) //if any elements in line are not '0'
{
using(StreamWriter outfile = new StreamWriter(MY_PATH, true))
{
foreach(char a in someString)
{
outfile.Write(WIDTH + " " + WIDTH, num1Str+"\t", num2Str+"\t");
}
}
}
if all elements in line are "0"
// do not write anything to the file, clear outfile buffer
Then why don't you check your line's content, before you write it ?
// process the line
string line = String.Format(WIDTH + " " + WIDTH, num1Str+"\t", num2Str+"\t");
if(!line.Trim().All(c => c == '0'))
outfile.Write(line);
I have two text files, Source.txt and Target.txt. The source will never be modified and contain N lines of text. So, I want to delete a specific line of text in Target.txt, and replace by an specific line of text from Source.txt, I know what number of line I need, actually is the line number 2, both files.
I haven something like this:
string line = string.Empty;
int line_number = 1;
int line_to_edit = 2;
using StreamReader reader = new StreamReader(#"C:\target.xml");
using StreamWriter writer = new StreamWriter(#"C:\target.xml");
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
writer.WriteLine(line);
line_number++;
}
But when I open the Writer, the target file get erased, it writes the lines, but, when opened, the target file only contains the copied lines, the rest get lost.
What can I do?
the easiest way is :
static void lineChanger(string newText, string fileName, int line_to_edit)
{
string[] arrLine = File.ReadAllLines(fileName);
arrLine[line_to_edit - 1] = newText;
File.WriteAllLines(fileName, arrLine);
}
usage :
lineChanger("new content for this line" , "sample.text" , 34);
You can't rewrite a line without rewriting the entire file (unless the lines happen to be the same length). If your files are small then reading the entire target file into memory and then writing it out again might make sense. You can do that like this:
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
int line_to_edit = 2; // Warning: 1-based indexing!
string sourceFile = "source.txt";
string destinationFile = "target.txt";
// Read the appropriate line from the file.
string lineToWrite = null;
using (StreamReader reader = new StreamReader(sourceFile))
{
for (int i = 1; i <= line_to_edit; ++i)
lineToWrite = reader.ReadLine();
}
if (lineToWrite == null)
throw new InvalidDataException("Line does not exist in " + sourceFile);
// Read the old file.
string[] lines = File.ReadAllLines(destinationFile);
// Write the new file over the old file.
using (StreamWriter writer = new StreamWriter(destinationFile))
{
for (int currentLine = 1; currentLine <= lines.Length; ++currentLine)
{
if (currentLine == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
writer.WriteLine(lines[currentLine - 1]);
}
}
}
}
}
If your files are large it would be better to create a new file so that you can read streaming from one file while you write to the other. This means that you don't need to have the whole file in memory at once. You can do that like this:
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
int line_to_edit = 2;
string sourceFile = "source.txt";
string destinationFile = "target.txt";
string tempFile = "target2.txt";
// Read the appropriate line from the file.
string lineToWrite = null;
using (StreamReader reader = new StreamReader(sourceFile))
{
for (int i = 1; i <= line_to_edit; ++i)
lineToWrite = reader.ReadLine();
}
if (lineToWrite == null)
throw new InvalidDataException("Line does not exist in " + sourceFile);
// Read from the target file and write to a new file.
int line_number = 1;
string line = null;
using (StreamReader reader = new StreamReader(destinationFile))
using (StreamWriter writer = new StreamWriter(tempFile))
{
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
writer.WriteLine(line);
}
line_number++;
}
}
// TODO: Delete the old file and replace it with the new file here.
}
}
You can afterwards move the file once you are sure that the write operation has succeeded (no excecption was thrown and the writer is closed).
Note that in both cases it is a bit confusing that you are using 1-based indexing for your line numbers. It might make more sense in your code to use 0-based indexing. You can have 1-based index in your user interface to your program if you wish, but convert it to a 0-indexed before sending it further.
Also, a disadvantage of directly overwriting the old file with the new file is that if it fails halfway through then you might permanently lose whatever data wasn't written. By writing to a third file first you only delete the original data after you are sure that you have another (corrected) copy of it, so you can recover the data if the computer crashes halfway through.
A final remark: I noticed that your files had an xml extension. You might want to consider if it makes more sense for you to use an XML parser to modify the contents of the files instead of replacing specific lines.
When you create a StreamWriter it always create a file from scratch, you will have to create a third file and copy from target and replace what you need, and then replace the old one.
But as I can see what you need is XML manipulation, you might want to use XmlDocument and modify your file using Xpath.
You need to Open the output file for write access rather than using a new StreamReader, which always overwrites the output file.
StreamWriter stm = null;
fi = new FileInfo(#"C:\target.xml");
if (fi.Exists)
stm = fi.OpenWrite();
Of course, you will still have to seek to the correct line in the output file, which will be hard since you can't read from it, so unless you already KNOW the byte offset to seek to, you probably really want read/write access.
FileStream stm = fi.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
with this stream, you can read until you get to the point where you want to make changes, then write. Keep in mind that you are writing bytes, not lines, so to overwrite a line you will need to write the same number of characters as the line you want to change.
I guess the below should work (instead of the writer part from your example). I'm unfortunately with no build environment so It's from memory but I hope it helps
using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite)))
{
var destinationReader = StreamReader(fs);
var writer = StreamWriter(fs);
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
destinationReader .ReadLine();
}
line_number++;
}
}
The solution works fine. But I need to change single-line text when the same text is in multiple places. For this, need to define a trackText to start finding after that text and finally change oldText with newText.
private int FindLineNumber(string fileName, string trackText, string oldText, string newText)
{
int lineNumber = 0;
string[] textLine = System.IO.File.ReadAllLines(fileName);
for (int i = 0; i< textLine.Length;i++)
{
if (textLine[i].Contains(trackText)) //start finding matching text after.
traced = true;
if (traced)
if (textLine[i].Contains(oldText)) // Match text
{
textLine[i] = newText; // replace text with new one.
traced = false;
System.IO.File.WriteAllLines(fileName, textLine);
lineNumber = i;
break; //go out from loop
}
}
return lineNumber
}