I am working on a website ATM school project, and I need to be able to rewrite the account balance in the original .txt file based on session transactions. The .txt file contains 3 lines, each with (username),(password),(balance). The issue I'm having is that the program cannot write to file while it is still reading it. The loop works fine if I write to a different file, but I have to edit the original (so the updated balance is retained the next time the program is run). Below is the code from the Logout page load event.
//Stream variable
StreamReader readFile;
StreamWriter writeFile;
//Counter variable
int index = 0;
//Open file
readFile = File.OpenText(#"C:\C#\Project4_TSullivan\loginFile.txt");
//Array rows
const int ROWS = 3;
while (index < ROWS && !readFile.EndOfStream)
{
string str = readFile.ReadLine();
string[] tokens = str.Split(',');
//Check if username matches session username
if (tokens[0] == Convert.ToString(Session["sessionUserName"]))
{
//Update balance
tokens[2] = Convert.ToString(Session["sessionBalance"]);
}
if (index == 0)
{
writeFile = File.CreateText(#"C:\C#\Project4_TSullivan\loginFile.txt");
writeFile.WriteLine(tokens[0] + "," + tokens[1] + "," + tokens[2]);
writeFile.Close();
}
else
{
writeFile = File.AppendText(#"C:\C#\Project4_TSullivan\loginFile.txt");
writeFile.WriteLine(tokens[0] + "," + tokens[1] + "," + tokens[2]);
writeFile.Close();
}
index++;
}
//Close file
readFile.Close();
Please let me know if any additional info would be helpful. I may be going about this in entirely the wrong way, and any advice would be much appreciated. Thanks!
Edited
Here is the solution in case the mods want to save it. Using File.ReadAllLines I was able to store the contents of the file in an array. Then I created 3 more arrays (1 for each line in the file) to tokenize each line.
string path = #"C:\C#\Project4_TSullivan\loginFile.txt";
string[] readFile = File.ReadAllLines(path);
string[] token0 = readFile[0].Split(',');
string[] token1 = readFile[1].Split(',');
string[] token2 = readFile[2].Split(',');
if (token0[0] == Convert.ToString(Session["sessionUsername"]))
{
token0[2] = Convert.ToString(Session["sessionBalance"]);
}
else if (token1[0] == Convert.ToString(Session["sessionUsername"]))
{
token1[2] = Convert.ToString(Session["sessionBalance"]);
}
else
{
token2[2] = Convert.ToString(Session["sessionBalance"]);
}
using (StreamWriter writeFile = File.CreateText(path))
{
writeFile.WriteLine(token0[0] + "," + token0[1] + "," + token0[2]);
writeFile.WriteLine(token1[0] + "," + token1[1] + "," + token1[2]);
writeFile.WriteLine(token2[0] + "," + token2[1] + "," + token2[2]);
}
Related
I am trying to increment the filename (e.g., "file1.csv", "file2.csv", etc.), each time a new file is generated. I followed this thread Increment the file name if the file already exists in c# but the solution is not useful for my case. What I want to do is check if the file exists in the first place and if it does write in it. If it doesn't create one and write. The problem is that if the file exists but it's from another user, I want the system to increment the file number and not write to the same file just because it exists. What I have so far:
public void saveFile()
{
int count = 0;
string title = "TimeStamp,Name,Trial,Time_spent-dist,Time_spent_tar\n";
string output = System.DateTime.Now.ToString("mm_ss_ffff") + "," +
currentScene.name.ToString() + "," +
trialNum.ToString() + "," +
timerDistractor.ToString() + "," +
timerTarget.ToString();
string fname = "User_" + count + ".csv";
string path = Path.Combine(Application.persistentDataPath, fname);
if (File.Exists(path))
{
File.AppendAllText(path, "\n" + output);
}
else
{
StreamWriter writer = new StreamWriter(path);
writer.WriteLine(title + "\n" + output);
writer.Close();
}
}
Any pointers?
I make an application that must be able to process and store a lot of measurements.
There can be up to 129600 measurements and every measurement can have 1499 values.
All data is stored in an array, each column is a measurement.
After 10 measurements I save the data from the array to a file. Because if the application crashes, then i haven't lost all data.
In the beginning of the measurements, saving to the file only takes a few millieseconds. But when i am at measurement 200, it already takes 13 seconds. The time to save to the file keeps increasing. This makes sense, of course, because the array is getting bigger.
I can choose to only save every 50 or 100 measurements. But saving to the file will still take a long time.
Below you can see what the data in the file looks like:
Frequency(Hz);S21(dB)_1;S21(dB)_2
10000000;-42.87726786;-35.66746585
79950000;-60.5887682;-63.55421833
149900000;-84.56555597;-74.36793049
219850000;-76.71335093;-80.68063652
289800000;-68.96360042;-68.41861962
359750000;-71.24272337;-74.90287556
429700000;-78.65528362;-75.50614099
...
...
13860100000;-85.80839142;-84.03051407
13930050000;-79.31238317;-82.87468675
14000000000;-88.9003575;-75.48071161
Is there a possibility to work in a different way, In such way that it takes less time to save?
The code below converts the array to a large string.
measurementArrayToSave is a 2D array where each measurement data is added.
private string StringToSave() // Create a large string from the array.
{
// Create a file to write to.
string writeToFile = "";
writeToFile = writeToFile + "Frequency(Hz)"; // Title is depending on the number of measurements.
for (int titles = 0; titles < (numberOfMeasurements); titles++)
{
writeToFile = writeToFile + ";S21(dB)_" + (titles + 1).ToString() + " ";
}
writeToFile = writeToFile + ";Average" + "\n";
for (int i = 0; i < (Points + 1); i++) // write array with measurements to string
{
int column = 0;
if (i == Points)
{
writeToFile = writeToFile + "Average"; // Set name "Average" on the last line
column = 1;
}
for (; column < (numberOfMeasurements + 2); column++)
{
if (column == 0)
{
writeToFile = writeToFile + measurementArrayToSave[column, i].ToString().Replace(",", ".");
}
else
{
writeToFile = writeToFile + ";" + measurementArrayToSave[column, i].ToString().Replace(",", ".");
}
}
writeToFile = writeToFile + "\n";
}
return writeToFile;
}
The code below save the string to a file.
private void SaveMeasurementStirrerAutomatic() // automatic save without dialogBox
{
// Full name will depend on date and time.
string path = #"..\\measurementFiles\\" + textBoxName.Text +"_"+ thisDay.ToString("yyyy_MM_dd_HH_mm_ss") + ".txt";
if (File.Exists(path))
{
File.Delete(path);
}
string writeToFile = StringToSave(); // call StringToSave()
File.WriteAllText(path, writeToFile); // write string to file.
labelStatus.Invoke((MethodInvoker)delegate { labelStatus.Text = "Data is saved!"; });
}
Take a look at File.AppendText method:
// This text is always added, making the file longer over time
// if it is not deleted.
using (StreamWriter sw = File.AppendText(path))
{
sw.WriteLine("This");
sw.WriteLine("is Extra");
sw.WriteLine("Text");
}
#techgirl2000 you are probably using the entire history on every single "Save" operation.
You better use that in-memory array as a temporary buffer. i.e. the data in-memory should be "appended" to the file, AND cleared once the save is successful.
private void SaveMeasurementStirrerAutomatic() // automatic save without dialogBox
{
// Full name will depend on date and time.
string path = #"..\\measurementFiles\\" + textBoxName.Text +"_"+ thisDay.ToString("yyyy_MM_dd_HH_mm_ss") + ".txt";
// Create the file, only if needed
if (!File.Exists(path))
{
string headers = HeadersToSave(); // Get the first line (Headers)
File.WriteAllText(path, headers); // write string to file.
}
// append new lines
string writeToFile = StringToSave(); // call StringToSave()
using (StreamWriter sw = File.AppendText(path))
{
sw.Write(writeToFile); // not sure about this
}
//TODO: clear the array in memory
labelStatus.Invoke((MethodInvoker)delegate { labelStatus.Text = "Data is saved!"; });
}
Now you need to split the StringToSave() into another function with only the header line
private string HeadersToSave() // Create a large string from the array.
{
// Create a file to write to.
string writeToFile = "Frequency(Hz)"; // Title is depending on the number of measurements.
for (int titles = 0; titles < (numberOfMeasurements); titles++)
{
writeToFile = writeToFile + ";S21(dB)_" + (titles + 1).ToString() + " ";
}
writeToFile = writeToFile + ";Average" + "\n";
return writeToFile;
}
// ...
If you can't clear the data, then use a marker on every row to confirm if that specific line was appended.
I have a textbox that contains all of the lines of a loaded file.
It looks like this:
I am able to load a specific line of the file that contains a specific string using this in the app:
How would I be able to update the file/main textbox after I press the "Edit Module" button, if any of the textboxes would be changed .
For example, I would change Exam Weighting: "0.4" to Exam Weighting: "0.6", then press the "Edit Module" button which would edit the main textbox(file content). Which then would allow me to save the file with the updated content.
This is the code I am using to get a specific line from the file based on string from a textbox:
private void editModuleButton_Click(object sender, EventArgs e)
{
citation = editModuleComboBox.Text;
citationChange();
}
private void citationChange()
{
List<string> matchedList = new List<string>();
string[] linesArr = File.ReadAllLines(fileName);
//find matches
foreach (string s in linesArr)
{
if (s.Contains(citation))
{
matchedList.Add(s); //matched
}
}
//output
foreach (string s in matchedList)
{
string citationLine = s;
string[] lineData = citationLine.Split(',');
selectedModuleLabel.Text = lineData[2];
moduleTitleTextBox.Text = lineData[3];
creditsTextBox.Text = lineData[4];
semesterTextBox.Text = lineData[5];
examWeightingTextBox.Text = lineData[6];
examMarkTextBox.Text = lineData[7];
testWeightingTextBox.Text = lineData[8];
testMarkTextBox.Text = lineData[9];
courseworkWeightingTextBox.Text = lineData[10];
courseworkMarkTexbox.Text = lineData[11];
}
}
If somebody with enough rep could insert the images to this post, that would be great. Thanks
This solution might not be the perfect, but should work for you. What you need to do is whenever the Edit Module button is pressed, create a new string based on the text fields and replace it with the original line. First declare a string variable private string ChangedString = ""; inside the class, then:
foreach (string s in matchedList)
{
string citationLine = s;
string[] lineData = citationLine.Split(',');
string Stream = lineData[0]; //Store this somewhere so that it can be accessed later
string Stage = lineData[1]; //Store this somewhere so that it can be accessed later
selectedModuleLabel.Text = lineData[2];
moduleTitleTextBox.Text = lineData[3];
creditsTextBox.Text = lineData[4];
semesterTextBox.Text = lineData[5];
examWeightingTextBox.Text = lineData[6];
examMarkTextBox.Text = lineData[7];
testWeightingTextBox.Text = lineData[8];
testMarkTextBox.Text = lineData[9];
courseworkWeightingTextBox.Text = lineData[10];
courseworkMarkTexbox.Text = lineData[11];
}
store Stream and Stage in any Textbox/ComboBox if you already haven't then replace them accordingly in the following line. Now in EditButton_Click [Click Event] write:
ChangedString = Stream + "," + Stage + "," + selectedModuleLabel.Text + "," + moduleTitleTextBox.Text
+ "," + creditsTextBox.Text + "," + semesterTextBox.Text + "," + examWeightingTextBox.Text + ","
+ examMarkTextBox.Text + "," + courseworkWeightingTextBox.Text + "," + courseworkMarkTexbox.Text;
Now replace this string with the original line.
Edit: As you would get the line number which is being edited, store it in a variable, let's say
int LineBeingEdited = 3 //Supposing line number three is being edited.
Then again in the same Click event you can write this:
ChangedString = Stream + "," + Stage + "," + selectedModuleLabel.Text + "," + moduleTitleTextBox.Text
+ "," + creditsTextBox.Text + "," + semesterTextBox.Text + "," + examWeightingTextBox.Text + ","
+ examMarkTextBox.Text + "," + courseworkWeightingTextBox.Text + "," + courseworkMarkTexbox.Text;
var lines = TextBox1.Lines;
lines[LineBeingEdited] = ChangedString;
TextBox1.Lines = lines;
EDIT 2: To get the line number I would suggest you to modify your for each loop to for loop. Also add a int variable to store the line number inside the class like : private int LineBeingEdited = 0;
Modify this for each :
foreach (string s in linesArr)
{
if (s.Contains(citation))
{
matchedList.Add(s); //matched
}
}
To for loop:
for (int a = 0; a < linesArr.Length; a++)
{
if (s.Contains(citation))
{
matchedList.Add(linesArr[a]); //matched
LineBeingEdited = a;
break; //breaks the loop when a match is found
}
}
The above method is being used, taking into consideration that there will always be a single match. LineBeingEdited will now have the line number and can be accessed from anywhere in the class
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(#"" + textBox2.Text + #"\" + filename.TrimStart() + ".csv", true))
{
if (!exists)
{
writer.WriteLine(DateTime.Now.ToLongDateString());
writer.WriteLine("REG.,BR.,BR.NAME,AC TYPE,PRODUCT,NO.OF ACS,ORG.CURRENCY BALANCE,ORG CURRENCY,BALANCE LKR");
writer.WriteLine(text.Replace("|", ","));
}
writer.WriteLine(text.Replace("|", ","));
////true is append parameter. I use this code to create Excel files. I want add new column and fill each cell with auto increment numbers.
As you didn't include the appropriate infos I take it that text includes all lines that you want to use and , is being used as the separator instead of the more commonly used ; .
The following splits this complete text into multiple lines and creates an "autoincrement" number that is appended as the last column.
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(#"" + textBox2.Text + #"\" + filename.TrimStart() + ".csv", true))
{
if (!exists)
{
writer.WriteLine(DateTime.Now.ToLongDateString());
writer.WriteLine("REG.,BR.,BR.NAME,AC TYPE,PRODUCT,NO.OF ACS,ORG.CURRENCY BALANCE,ORG CURRENCY,BALANCE LKR");
}
var textArray = text.Replace("|", ",").split(Environment.NewLine);
int number = 0;
foreach (string text in textArray)
{
number ++;
write.WriteLine(text + "," + number.ToString());
}
I read data from a text file which is 27 MB file and contains 10001 rows, I need to handle large data. I perform some kind of processing in each row of data and then write it back to a text file. This is the code I have am using
StreamReader streamReader = System.IO.File.OpenText("D:\\input.txt");
string lineContent = streamReader.ReadLine();
int count = 0;
using (StreamWriter writer = new StreamWriter("D:\\ft1.txt"))
{
do
{
if (lineContent != null)
{
string a = JsonConvert.DeserializeObject(lineContent).ToString();
string b = "[" + a + "]";
List<TweetModel> deserializedUsers = JsonConvert.DeserializeObject<List<TweetModel>>(b);
var CreatedAt = deserializedUsers.Select(user => user.created_at).ToArray();
var Text = deserializedUsers.Where(m => m.text != null).Select(user => new
{
a = Regex.Replace(user.text, #"[^\u0000-\u007F]", string.Empty)
.Replace(#"\/", "/")
.Replace("\\", #"\")
.Replace("\'", "'")
.Replace("\''", "''")
.Replace("\n", " ")
.Replace("\t", " ")
}).ToArray();
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
writer.WriteLine(TextWithTimeStamp);
}
lineContent = streamReader.ReadLine();
}
while (streamReader.Peek() != -1);
streamReader.Close();
This code helps does well up to 54 iterations as I get 54 lines in the output file. After that it gives error "Index was outside the bounds of the array." at line
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
I am not very clear about the issue if the maximum capacity of array has been violated, if so how can I increase it or If I can write the individual line encountered in loop through
writer.WriteLine(TextWithTimeStamp);
And clean the storage or something that can solve this issue. I tried using list insead of array , still issue is the same.Please help.
Change this line
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
to
var TextWithTimeStamp = (Text.Any() ? Text.First().a : string.Empty) +
" (timestamp:" + (CreatedAt.Any() ? CreatedAt.First() : string.Empty) + ")";
As you are creating Text and CreatedAt collection objects, they might be empty (0 total item) based on some scenarios and conditions.
Those cases, Text[0] and CreatedAt[0] will fail. So, before using the first element, check if there are any items in the collection. Linq method Any() is used for that purpose.
Update
If you want to skip the lines that do not contain text, change this lines
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
writer.WriteLine(TextWithTimeStamp);
to
if (Text.Any())
{
var TextWithTimeStamp = Text.First().a + " (timestamp:" + CreatedAt.First() + ")";
writer.WriteLine(TextWithTimeStamp);
}
Update 2
To include all the stringss from CreatedAt rather than only the first one, you can add all the values in comma separated strings. A general example
var strings = new List<string> { "a", "b", "c" };
var allStrings = string.Join(",", strings); //"a,b,c"