Game console as StringBuilder, String[] or something else? - c#

I'm making an XNA game console which should draw the output lines as they are added. I want to draw only the last 10 lines and I can't figure out how to produce the actual output string.
I was trying to use StringBuilder to collect all lines in it and then read the last lines into a String, but it turns out StringBuilder doesn't have a specific line retrieval method. I tried using String[], but it adds significant lag increasing over time. How should I do this?

If you no longer need messages older than 10 lines, you could use a typed Queue.
Depending on how you're rendering it, it might be the easiest way of managing that data.

Related

Call Length Property on Returned Array in Chained String/LINQ Methods of C#

I found this post on selecting a range from an array, and have to use the LINQ option:
Selecting a range of items inside an array in C#
Ultimately, I'm trying to get the last four lines from some text file. After, I've read in and cleaned the lines for unwanted characters and empty lines, I have an array with all of the lines. I'm using the following to do so:
string[] allLines = GetEachLine(results);
string[] lastFourLines = allLines.Skip(allLines.Length - 4).Take(4).ToArray();
This works fine, but I'm wondering if I could somehow skip assinging to the allLines variable all together. Such as:
string[] lastFourLines = GetEachLine(results).Skip(returnedArrayLength - 4).Take(4).ToArray();
It would be better to change GetEachLine and code preceding it (however results is computed) to use IEnumerable<T> and avoid using an array to read the entire file in memory for the last four lines (unless you use all of results for something else) - consider using File.ReadLines.
However, if you are using .Net Core 2.0 or greater, you can use Enumerable.TakeLast to efficiently return the last four lines:
var lastFourLines = GetEachLine(results).TakeLast(4);
if GetEachLine() returns string[] then that should work fine, though null checking may be needed.
As you chain more you may want to use line breaks to increase readability:
string[] lastFourLines = GetEachLine(results)
.Skip(allLines.Length - 4)
.Take(4)
.ToArray();
allLines.Length won't exist unless you still have line 1 from your question, you can avoid calling GetEachLine() twice by using TakeLast().
string[] lastFourLines = GetEachLine(results)
.TakeLast(4)
.ToArray();
If you are looking to efficiently retrieve the last N (filtered) line of a large file, you really need to start at the point where you are reading the file contents.
Consider a 1GB log file containing 10M records, where you only want the last few lines. Ideally, you would want to start by reading the last couple KB and then start extracting lines by searching for line breaks from the end, extracting each line and returning them in an iterator yield. If you run out of data, read the preceding block. Continue only as long as the consumer requests more values from the iterator.
Offhand, I don't know a built-in way to do this, and coding this from scratch could get pretty involved. Luckily, a search turned up this similar question having a highly rated answer.

C# NAudio - Keep Last X Seconds Of Audio

I have a project where audio is recorded at several sources, and the goal is to process X (user-defined) seconds of it through various methods (DSP as well as speech-to-text).
I'm using a MixingSampleProvider to collect all the sources into one "provider." I'm passing this to a NotifyingSampleProvider because it raises an event at each sample, and then passing that sample to my class that does the processing. I'm adding the float the NotifyingSampleProvider produces to the end of my "X second window" array (using Array.Copy to create a temp array with all but that last value, adding that last value, and copying the temp array back to the original) which I use for processing.
The obvious problem here is that it notifies (and that I'm locking and adding to the "X second window" array) for every single sample, or 44100 times a second. This leads to the audio being pretty much constantly locked so it can't be processed. There's got to be a more performant way to deal with this.
The one idea I had was a BufferedWaveProvider that doesn't get read from anywhere else, so it's always full (with DiscardOnOverflow = true of course). However A) this still requires a NotifyingSampleProvider to add to it periodically (you can't pass a provider to the BufferedWaveProvider for it to automatically read from) and I'd like to get away from such frequent (44100 Hz) function calls, and B) I understand that there's a limit to the BufferDuration length, which might be too small of a window (I don't recall what the limit is and I can't find anything online saying what it is).
How might I solve this problem? Using NAudio, how to I keep the last X seconds of audio accessible to a separate class at any time without using the NotifyingSampleProvider?

Advanced C# pattern search in long string (100-25000 char)

Let me start with this: I can't zip it or anything similar.
What I'm trying to do is search through fairly large strings. I use data blocks that look like 0g12h. (The 0 is the color from my palette. The g is a space to divide the numbers. The 12 means 12 pixels in a row use that color. The h is to divide the numbers again.)
The problem I'm having is that the blocks aren't all the same length. They range from 0g1h to 2546g115h. Basically I want to create a palette of common patterns to hopefully save space. Say I have: 12g345h19g12h190g11h occurring at least three times, then I could save space if I had something like: a=12g345h19g12h190g11h in the palette array and just put 'a' in the string. Or even not look at the data blocks, as you see in the attached file you get g640h a ton of times.
I could be wrong, but I'm pretty sure this could work. If you have a better idea how I could save space and not lose data, I'm more than open to the ideas.
Here is a great example since you can visually see the pattern: http://pastebin.com/5dbhxZQK. I chose this file because I knew it would have massive redundancy; most aren't this simple.
You could use a dictionary (probably Dictionary<string, int> and just could how many times each pattern occurs, then go back and rewrite the string with the appropriate replacements.
However, I would recommend that you read up a little about compression algorithms, what you are implementing appears to be a Run Length Encoding (RLE) scheme. You are then trying to compress again on top of that, consider looking at how Sliding Window compression works (which is what GZIP does) as an alternative to your RLE. Or look at Huffman encoding as a mechanism to reduce the amount of space needed for the codewords that you are creating (in simple terms Huffman encoding uses shorter symbols for more frequent patterns and longer symbols for less frequent patterns in a 'optimal' way)
This is a fun problem space to play in! Good Luck!

Writing huge amounts of text to a textbox

I am writing a log of lots and lots of formatted text to a textbox in a .net windows form app.
It is slow once the data gets over a few megs. Since I am appending the string has to be reallocated every time right? I only need to set the value to the text box once, but in my code I am doing line+=data tens of thousands of times.
Is there a faster way to do this? Maybe a different control? Is there a linked list string type I can use?
StringBuilder will not help if the text box is added to incrementally, like log output for example.
But, if the above is true and if your updates are frequent enough it may behoove you to cache some number of updates and then append them in one step (rather than appending constantly). That would save you many string reallocations... and then StringBuilder would be helpful.
Notes:
Create a class-scoped StringBuilder member (_sb)
Start a timer (or use a counter)
Append text updates to _sb
When timer ticks or certain counter reached reset and append to
text box
restart process from #1
No one has mentioned virtualization yet, which is really the only way to provide predictable performance for massive volumes of data. Even using a StringBuilder and converting it to a string every half a second will be very slow once the log gets large enough.
With data virtualization, you would only hold the necessary data in memory (i.e. what the user can see, and perhaps a little more on either side) whilst the rest would be stored on disk. Old data would "roll out" of memory as new data comes in to replace it.
In order to make the TextBox appear as though it has a lot of data in it, you would tell it that it does. As the user scrolls around, you would replace the data in the buffer with the relevant data from the underlying source (using random file access). So your UI would be monitoring a file, not listening for logging events.
Of course, this is all a lot more work than simply using a StringBuilder, but I thought it worth mentioning just in case.
Build your String together with a StringBuilder, then convert it to a String using toString(), and assign this to the textbox.
I have found that setting the textbox's WordWrap property to false greatly improves performance, as long as you're ok with having to scroll to the right to see all of your text. In my case, I wanted to paste a 20-50 MB file into a MultiLine textbox to do some processing on it. That took several minutes with WordWrap on, and just several seconds with WordWrap off.

C# Application Becomes Slow and Unresponsive as String in Multiline Textbox Grows

I have a C# application in which a LOT of information is being added to a Textbox for display to the user. Upon processing of the data, almost immediately, the application becomes very slow and unresponsive. This is how I am currently attempting to handle this:
var saLines = textBox1.Lines;
var saNewLines = saLines.Skip(50);
textBox1.Lines = saNewLines.ToArray();
This code is run from a timer every 100mS. Is there a better way to handle this? I am using Microsoft Visual C# 2008 Express Edition. Thanks.
The simple answer is TextBox.AppendText().
You get much better performance initially.
I tested writing a 500 char message every 20 ms for 2 mins (with BackgroundWorker) and the UI remained responsive and CPU minimal. At some point, of course, it will become unresponsive but it was good enough for my needs.
Try by having in memory a list with the content, and removing the first 50 elements by RemoveRange and then going with ToArray();
Like this :
lst.RemoveRange(0,50);
textBox1.Lines = lst.ToArray();
It should be a lot faster.
I'd say your main problem here is that you are using the TextBox as your primary storage for your text. Everytime you call TextBox.Lines, the string is split on Environment.NewLine.
Try turning it around:
Store the text in a new List<String>(maxLines)
In your AddLine method, check the length of your text buffer and use RemoveRange(0, excessCount)
Update your display TextBox by calling String.Join(Environment.NewLine, textBuffer.ToArray())
That last call is a bit expensive, but it should stop your slowdowns. To get it any faster you'd need to use a statically sized string array and move the references around yourself.
The most efficient way to trim an array is to create a new array of the desired size, then use Array.Copy to copy the desired portion of the old array.
I would recommend that you maintain a List<string> containing all of your lines.
You should use a StringBuilder to build a string containing the lines you're looking for, and set the textbox's Text proeprty to the StringBuilder's string. For added performance, set the StringBuilder's capacity to a reasonable guess of the final sie of the string. (Or to list.Skip(...).Take(...).Sum(s => s.Length))
If you're concerned about memory, you can trim the List<string> by calling RemoveRange.
As long as you don't put too much in the textbox at once, doing it this way should be extremely fast. All of the manipulation of the List<string> and the StringBuilder can be done in a background thread, and you can pass the completed string to the UI thread.
The TextBox.Lines property simply concatenates the array you give it using a StringBuilder, so there's no point in using it (and making a needless array).
Instead of splitting the text and then re-joining it, just get the sub-string from the 51st line:
int i = textBox1.GetFirstCharIndexFromLine(50);
if (i > 0) textBox1.Text = textBox1.Text.Substring(i);

Categories

Resources