I'm working in c# and wondering if it's possible to read back multiple variables and load into an array using Streamreader.Readline()?
Here's an example:
I have an array of different types being written using Streamwriter
foreach (Stuff stf in StuffArray)
{
sw.WriteLine(" " + stf.car+ " " + stf.carOwned + " " + stf.carLocation);
}
sw.Close();
It writes out a text line that looks like:
Magnum True Alabama
When I go to read it back the only option I have is to read the entire line with Streamreader.
I want to load it back like:
for (int i = 0; i < stfArray.Length; i++)
{
stfArray[i] = new stfArray(Readline spot 1, Readline spot 2, Readline spot3)
}
So I can put the stuff back into the array or a new array in the exact same way it was when I extracted it.
Thanks!
Read the line, parse it (say with string.Split) and then take the tokens that you've parse and rehydrate your variables.
You probably want to use a more sophisticated format that word, space, etc. (think of cars whose model name contains a space, or places like New York). Pick a separator that will not occur in your strings.
Or better still, pick a well known serialization format like XML or JSON.
Since you are creating string representation on your own, you are 100% sure about its format. Under any other occasion, you should consider something more robust, like serialization to XML and then writing it to a file.
In your situation I would recommend creating new collection of Stuff and add object, that lies in every line of your file, like:
List<Stuff> stuffItems = new List<Stuff>();
while(!streamReader.EndOfStream)
{
string[] line = streamReader.ReadLine().Split(' ');
stuffItems.Add(new Stuff(line[0], line[1], line[2]));
}
And now you can use this list however you want, for example overwrite old one calling ToArray method on list, etc.
But, again, I warn you about this approach: when you change your format, for example change delimiting character, you'd get exception :)
This might be a hard question, or a stupidly simple one. Either way, bear with me, as I'm quite new to C#.
I've been building a Form with a ListBox inside, and a few other things, and all has been well. Where things get odd are in the filling of said box with information.
My logic to filling this ListBox thus far has been:
foreach (var value in userList)
{
listBoxForForm.Items.Add("#" + value.internalId + " \t\t-\t" + value.title + "\t\t-\t" + value.typeName);
}
(Don't mind the odd between the " and the \t!)
The problem is that this looks odd if certain values are too long (not "long" as in value.length but "long" as in width).
To better explain it, I took a screenshot (forgive the government-style censoring):
As you can see, the problem is that the text doesn't seem to format correctly when using \t in this context.
Is there a way I can format this text neatly in the intended 3-column style I'm going for here? Without knowing the length of the variables in each value, I mean?
You should try with String.PadLeft():
using System;
class Program {
static void Main()
{
string s = "cat".PadRight(10); string s2 = "poodle".PadRight(10);
Console.Write(s);
Console.WriteLine("feline");
Console.Write(s2);
Console.WriteLine("canine");
}
}
which will output:
cat feline
poodle canine
Rather than using tabs, you should use a String.PadRight(X) where X is the maximum length of the field. You will need to ensure that your X value is longer than the maximum length of the data, or clip the data to the maximum length to ensure that the columns line up.
I think what you are looking for is this.
String.PadRight and String.PadLeft Methods
Short Question
I have a loop that runs 180,000 times. At the end of each iteration it is supposed to append the results to a TextBox, which is updated real-time.
Using MyTextBox.Text += someValue is causing the application to eat huge amounts of memory, and it runs out of available memory after a few thousand records.
Is there a more efficient way of appending text to a TextBox.Text 180,000 times?
Edit I really don't care about the result of this specific case, however I want to know why this seems to be a memory hog, and if there is a more efficient way to append text to a TextBox.
Long (Original) Question
I have a small app which reads a list of ID numbers in a CSV file and generates a PDF report for each one. After each pdf file is generated, the ResultsTextBox.Text gets appended with the ID Number of the report that got processed and that it was successfully processed. The process runs on a background thread, so the ResultsTextBox gets updated real-time as items get processed
I am currently running the app against 180,000 ID numbers, however the memory the application is taking up is growing exponentially as time goes by. It starts by around 90K, but by about 3000 records it is taking up roughly 250MB and by 4000 records the application is taking up about 500 MB of memory.
If I comment out the update to the Results TextBox, the memory stays relatively stationary at roughly 90K, so I can assume that writing ResultsText.Text += someValue is what is causing it to eat memory.
My question is, why is this? What is a better way of appending data to a TextBox.Text that doesn't eat memory?
My code looks like this:
try
{
report.SetParameterValue("Id", id);
report.ExportToDisk(ExportFormatType.PortableDocFormat,
string.Format(#"{0}\{1}.pdf", new object[] { outputLocation, id}));
// ResultsText.Text += string.Format("Exported {0}\r\n", id);
}
catch (Exception ex)
{
ErrorsText.Text += string.Format("Failed to export {0}: {1}\r\n",
new object[] { id, ex.Message });
}
It should also be worth mentioning that the app is a one-time thing and it doesn't matter that it is going to take a few hours (or days :)) to generate all the reports. My main concern is that if it hits the system memory limit, it will stop running.
I'm fine with leaving the line updating the Results TextBox commented out to run this thing, but I would like to know if there is a more memory efficient way of appending data to a TextBox.Text for future projects.
I suspect the reason the memory usage is so large is because textboxes maintain a stack so that the user can undo/redo text. That feature doesn't seem to be required in your case, so try setting IsUndoEnabled to false.
Use TextBox.AppendText(someValue) instead of TextBox.Text += someValue. It's easy to miss since it's on TextBox, not TextBox.Text. Like StringBuilder, this will avoid creating copies of the entire text each time you add something.
It would be interesting to see how this compares to the IsUndoEnabled flag from keyboardP's answer.
Don't append directly to the text property. Use a StringBuilder for the appending, then when done, set the .text to the finished string from the stringbuilder
Instead of using a text box I would do the following:
Open up a text file and stream the errors to a log file just in case.
Use a list box control to represent the errors to avoid copying potentially massive strings.
Personally, I always use string.Concat* . I remember reading a question here on Stack Overflow years ago that had profiling statistics comparing the commonly-used methods, and (seem) to recall that string.Concat won out.
Nonetheless, the best I can find is this reference question and this specific String.Format vs. StringBuilder question, which mentions that String.Format uses a StringBuilder internally. This makes me wonder if your memory hog lies elsewhere.
**based on James' comment, I should mention that I never do heavy string formatting, as I focus on web-based development.*
Maybe reconsider the TextBox? A ListBox holding string Items will probably perform better.
But the main problem seem to be the requirements, Showing 180,000 items cannot be aimed at a (human) user, neither is changing it in "Real Time".
The preferable way would be to show a sample of the data or a progress indicator.
When you do want to dump it at the poor User, batch string updates. No user could descern more than 2 or 3 changes per second. So if you produce 100/second, make groups of 50.
Some responses have alluded to it, but nobody has outright stated it which is surprising.
Strings are immutable which means a String cannot be modified after it is created. Therefore, every time you concatenate to an existing String, a new String Object needs to be created. The memory associated with that String Object also obviously needs to be created, which can get expensive as your Strings become larger and larger. In college, I once made the amateur mistake of concatenating Strings in a Java program that did Huffman coding compression. When you're concatenating extremely large amounts of text, String concatenation can really hurt you when you could have simply used StringBuilder, as some in here have mentioned.
Use the StringBuilder as suggested.
Try to estimate the final string size then use that number when instantiating the StringBuilder. StringBuilder sb = new StringBuilder(estSize);
When updating the TextBox just use assignment eg: textbox.text = sb.ToString();
Watch for cross-thread operations as above. However use BeginInvoke. No need to block
the background thread while the UI updates.
A) Intro: already mentioned, use StringBuilder
B) Point: don't update too frequently, i.e.
DateTime dtLastUpdate = DateTime.MinValue;
while (condition)
{
DoSomeWork();
if (DateTime.Now - dtLastUpdate > TimeSpan.FromSeconds(2))
{
_form.Invoke(() => {textBox.Text = myStringBuilder.ToString()});
dtLastUpdate = DateTime.Now;
}
}
C) If that's one-time job, use x64 architecture to stay within 2Gb limit.
StringBuilder in ViewModel will avoid string rebindings mess and bind it to MyTextBox.Text. This scenario will increase performance many times over and decrease memory usage.
Something that has not been mentioned is that even if you're performing the operation in the background thread, the update of the UI element itself HAS to happen on the main thread itself (in WinForms anyway).
When updating your textbox, do you have any code that looks like
if(textbox.dispatcher.checkAccess()){
textbox.text += "whatever";
}else{
textbox.dispatcher.invoke(...);
}
If so, then your background op is definitely being bottlenecked by the UI Update.
I would suggest that your background op use StringBuilder as noted above, but instead of updating the textbox every cycle, try updating it at regular intervals to see if it increases performance for you.
EDIT NOTE:have not used WPF.
You say memory grows exponentially. No, it is a quadratic growth, i.e. a polynomial growth, which is not as dramatic as an exponential growth.
You are creating strings holding the following number of items:
1 + 2 + 3 + 4 + 5 ... + n = (n^2 + n) /2.
With n = 180,000 you get total memory allocation for 16,200,090,000 items, i.e. 16.2 billion items! This memory will not be allocated at once, but it is a lot of cleanup work for the GC (garbage collector)!
Also, bear in mind, that the previous string (which is growing) must be copied into the new string 179,999 times. The total number of copied bytes goes with n^2 as well!
As others have suggested, use a ListBox instead. Here you can append new strings without creating a huge string. A StringBuild does not help, since you want to display the intermediate results as well.
how do I get text to wrap from one acrofield to the next? I have an adobe pdf doc our client gave us. It has acro fields one atop another (all with the same name). They want the text to wrap from one to another when it reaches the end of the line. All the other examples I see out there do not deal with filling in acro fields that wrap. Please help!
// loop through disabilities and display them
foreach (var disability in formNature.Disabilities)
{
fields.SetField("EVALUATION", disability.PrimaryDisabilityName + "; ");
}
in theory this should loop through all the disabilities they had entered on the web form and display them one after another while text-wrapping when it reaches the end of each line. But instead it only displays one item one the field.
This isn't a complete answer unfortunately.
First, when you call SetField() you are erasing the current contents of the field and replacing it with your new value. When done in a loop only the last value will ever be stored then. What you need to do is loop through each value and concatenate them into one big string.
string buf = '';
foreach (var disability in formNature.Disabilities)
{
buf += disability.PrimaryDisabilityName + "; ";
}
buf = buf.Trim();
Second, the PDF standard to the best of my knowledge does not support chaining of fields for overflow which is what you are looking for. The only way that I know of to accomplish what you are trying is to actually measure the strings and compare them to the widths of the fields and truncate them as needed. To do this you will need to find the font used for the given field, create a BaseFont from it and use that to Measure the string. Then compare that with the field's rectangle and use only the characters that "fit" into that field. Repeat as needed.
That all said, I would really really recommend that you just edit the PDF and replace the multiple fields with one large field that supports multiple lines. Your life will be much, much easier.
Well, now i have a new problem.
Im writing code in C#
I want to save from textBoxName into group.txt file each time i enter string into textbox and click on save button. It should save at this order (if its possible to sort it like A-Z that would be great):
1. Petar Milutinovic
2. Ljiljana Milutinovic
3. Stefan Milutinovic
4. ... etc
I cant get it to work, i tried to use tehniques from my first question, and no solution yet :(
This is easy one i guess, but im still a beginer and i need this baddly...
Try to tackle this from a top-down approach. Write out what should happen, because it's not obvious from your question.
Example:
User enters a value in a (single-line?) textbox
User clicks Save
One new line is appended to the end of a file, with the contents of the textbox in step 1
Note: each line is prefixed with a line number, in the form "X. Sample" where X is the line number and Sample is the text from the textbox.
Is the above accurate?
(If you just want to add a line to a text file, see http://msdn.microsoft.com/en-us/library/ms143356.aspx - File.AppendAllText(filename, myTextBox.Text + Environment.NewLine); may be what you want)
Here's a simple little routine you can use to read, sort, and write the file. There are loads of ways this can be done, mine probably isn't even the best. Even now I'm thinking "I could have written that using a FileStream and done the iteration for counting then", but they're micro-optimizations that can be done later if you have performance issues with multi-megabyte files.
public static void AddUserToGroup(string userName)
{
// Read the users from the file
List<string> users = File.ReadAllLines("group.txt").ToList();
// Strip out the index number
users = users.Select(u => u.Substring(u.IndexOf(". ") + 2)).ToList();
users.Add(userName); // Add the new user
users.Sort((x,y) => x.CompareTo(y)); // Sort
// Reallocate the number
for (int i = 0; i < users.Count; i++)
{
users[i] = (i + 1).ToString() + ". " + users[i];
}
// Write to the file again
File.WriteAllLines("group.txt", users);
}
If you need the file to be sorted every time a new line is added, you'll either have to load the file into a list, add the line, and sort it, or use some sort of search (I'd recommend a binary search) to determine where the new line belongs and insert it accordingly. The second approach doesn't have many advantages, though, as you basically have to rewrite the entire file in order to insert a line - it only saves you time in the best case scenario, which occurs when the line to be inserted falls at the end of the file. Additionally, the second method is a bit lighter on the processor, as you aren't attempting to sort every line - for small files however, the difference will be unnoticeable.