Overwriting Console line with various length texts - c#

I'm trying to display percentages of loading in the same place
and I found solution on that
Console.Write($"\r{ (double) (i+1) * 100 / list.Count }% - {text}");
but after the percentage I'd want to display some text which has different lengths e.g something between 20-40 characters
The problem with this approach is that if "new" line is shorter than "previous" then some part of "previous" text still remains there.
I managed to write 'hack' which overwrites current line with spaces (clears it) and then writes my line
Console.Write($"\r ");
Console.Write($"\r{ (double) (i+1) * 100 / list.Count }% - {text}");
Is there an better solution to do that?

The easiest way to do this is generally with
var stringOfLengthMaxWithSpacestoLeft = yourString.PadLeft(MaxStringLength, ' ');
or
var stringOfLengthMaxWithSpacestoRight = yourString.PadRight(MaxStringLength, ' ');
If you want to clear the line, all you have to do is use the backspace character and then overwrite with with the same length, i.e.
for (var i = 0; i++; i < MaxStringLength)
Console.Write("\b");
Then you can start writing again.

Related

how to add a sign between each letter in a string in C#?

I have a task, in which i have to write a function called accum, which transforms given string into something like this:
Accumul.Accum("abcd"); // "A-Bb-Ccc-Dddd"
Accumul.Accum("RqaEzty"); // "R-Qq-Aaa-Eeee-Zzzzz-Tttttt-Yyyyyyy"
Accumul.Accum("cwAt"); // "C-Ww-Aaa-Tttt"
So far I only converted each letter to uppercase and... Now that I am writing about it, I think it could be easier for me to - firstly multiply the number of each letter and then add a dash there... Okay, well let's say I already multiplied the number of them(I will deal with it later) and now I need to add the dash. I tried several manners to solve this, including: for and foreach(and now that I think of it, I can't use foreach if I want to add a dash after multiplying the letters) with String.Join, String.Insert or something called StringBuilder with Append(which I don't exactly understand) and it does nothing to the string.
One of those loops that I tried was:
for (int letter = 0; letter < s.Length-1; letter += 2) {
if (letter % 2 == 0) s.Replace("", "-");
}
and
for (int letter = 0; letter < s.Length; letter++) {
return String.Join(s, "-");
}
The second one returns "unreachable code" error. What am I doing wrong here, that it does nothing to the string(after uppercase convertion)? Also, is there any method to copy each letter, in order to increase the number of them?
As you say string.join can be used as long as an enumerable is created instead of a foreach. Since the string itself is enumerable, you can use the Linq select overload which includes an index:
var input = "abcd";
var res = string.Join("-", input.Select((c,i) => Char.ToUpper(c) + new string(Char.ToLower(c),i)));
(Assuming each char is unique or can be used. e.g. "aab" would become "A-Aa-Bbb")
Explanation:
The Select extension method takes a lambda function as parameter with c being a char and i the index. The lambda returns an uppercase version of the char (c) folowed by a string of the lowercase char of the index length (new string(char,length)), (which is an empty string for the first index). Finally the string.join concatenates the resulting enumeration with a - between each element.
Use this code.
string result = String.Empty;
for (int i = 0; i < s.Length; i++)
{
char c = s[i];
result += char.ToUpper(c);
result += new String(char.ToLower(c), i);
if (i < s.Length - 1)
{
result += "-";
}
}
It will be better to use StringBuilder instead of strings concatenation, but this code can be a bit more clear.
Strings are immutable, which means that you cannot modify them once you created them. It means that Replace function return a new string that you need to capture somehow:
s = s.Replace("x", "-");
you currently are not assigning the result of the Replace method anywhere, that's why you don't see any results
For the future, the best way to approach problems like this one is not to search for the code snippet, but write down step by step algorithm of how you can achieve the expected result in plain English or some other pseudo code, e.g.
Given I have input string 'abcd' which should turn into output string 'A-Bb-Ccc-Dddd'.
Copy first character 'a' from the input to Buffer.
Store the index of the character to Index.
If Buffer has only one character make it Upper Case.
If Index is greater then 1 trail Buffer with Index-1 lower case characters.
Append dash '-' to the Buffer.
Copy Buffer content to Output and clear Buffer.
Copy second character 'b' from the input to Buffer.
...
etc.
Aha moment often happens on the third iteration. Hope it helps! :)

Efficiency of string.Split() vs. string.Substring() in C#?

I'm working on a project that involves taking large text files and parsing each line. The point is to parse the whole text file into cells, much like an Excel spreadsheet. Unfortunately, there are no delimiters for most of the files, so I need some sort of index-based method to manually create the cells, even if the column is blank.
Previously, lines were parsed by splitting on null, which worked well. However, new data has made this method unreliable due to its not including blank cells, so I had to make a new method of parsing lines, which uses Substring. The method takes in an array of integers indices and splits the strings on the given indices:
private string[] SetCols3(int[] fixedWidthValues, string line)
{
{
string[] cols = new string[fixedWidthValues.Length];
int columnLength;
int FWV;
int FWV2;
bool lastOfFWV;
bool outOfBounds;
for (int x = 0; x < fixedWidthValues.Length; x++)
{
FWV = fixedWidthValues[x];
lastOfFWV = x + 1 >= fixedWidthValues.Length;
outOfBounds = lastOfFWV ? true : fixedWidthValues[x + 1] >= line.Length;
FWV2 = lastOfFWV || outOfBounds ? line.Length : fixedWidthValues[x + 1];
columnLength = FWV2 - FWV;
columnLength *= columnLength < 0 ? -1 : 1;
if (FWV < line.Length)
{
cols[x] = line.Substring(FWV, columnLength).Trim();
}
}
return cols;
}
Quick breakdown of the code: the integers and booleans are just to handle blank columns, lines that are shorter than normal, etc., and to make the code cleaner for other people to understand a little better (as opposed to one long, convoluted if statement).
My question: is there a way to make this more efficient? For some reason, this method takes significantly longer than the previous method. I understand it does more, so more time was expected. However, the difference is surprisingly huge. One iteration (with 15 indices) takes around 0.07 seconds (which is huge considering this method gets called several thousands time per file), compared to 0.00002 seconds on the high end for the method that splits on null. Is there something I can change in my code to noticeably increase its efficiency? I haven't been able to find anything particularly useful after hours of searching online.
Also, the number of indices/columns greatly affects the speed. For 15 columns, it takes around 0.07 seconds compared to 0.05 for 10 columns.
First,
outOfBounds = lastOfFWV ? true : fixedWidthValues[x + 1] >= line.Length;
could be changed to
outOfBounds = lastOfFWV || fixedWidthValues[x + 1] >= line.Length;
Next,
columnLength = FWV2 - FWV;
columnLength *= columnLength < 0 ? -1 : 1;
could be changed to
columnLength = Math.Abs(FWV2 - FWV);
And last,
if (FWV < line.Length)
{
could be moved to just after the FWV assignment at the top of the loop and changed to
if (FWV < line.Length)
continue;
But, I don't think any of these changes would make a significant impact on speed. Possibly more impact would be gained by changing what's passed in. Instead of passing in the column starting positions and calculating the column widths for each line, which won't change, pass in the starting positions and column widths. This way there's no calculation involved.
But rather than guessing, it'd be best to profile the method to find the hot spot(s).
The issue was two stray .ToInt32() calls I accidentally included (I don't know why they were there). This particular method was a different method, one from my company, than the Convert.ToInt32(), and for some reason it was majorly inefficient when trying to convert numbers. For reference, the issues was on the following lines as follows:
FWV = fixedWidthValues[x].ToInt32();
...
FWV2 = lastOfFWV || outOfBounds ? line.Length : fixedWidthValues[x + 1].ToInt32();
Removing them increased the efficiency by 60 times...

RichTextBox - sorting lines randomly

I want to write an application which sorts randomly line of text which I copy from a source and paste into RichTextBox area.
However, there is one condition - text is formatted (some words are in bold, underline etc.). So any suggestions? How should it look like?
I think I should use RichTextBox.Rtf or something but I am really a beginner and I appreciate every hint or example code.
Thanks
It is a bit tricky. You can retrieve the formatted RTF text lines like this
string[] rtfLines = new string[richTextBox1.Lines.Length];
for (int i = 0; i < rtfLines.Length; i++) {
int start = richTextBox1.GetFirstCharIndexFromLine(i);
int length = richTextBox1.Lines[i].Length;
richTextBox1.Select(start, length);
rtfLines[i] = richTextBox1.SelectedRtf;
}
Now you can shuffle the lines like this
var random = new Random();
rtfLines = rtfLines.OrderBy(s => random.NextDouble()).ToArray();
Clear the RichtTextBox
richTextBox1.Text = "";
Inserting the lines is best done in reverse order because it is easier to select the beginning of the text
// Insert the line which will be the last line.
richTextBox1.Select(0, 0);
richTextBox1.SelectedRtf = rtfLines[0];
// Prepend the other lines and add a line break.
for (int i = 1; i < rtfLines.Length; i++) {
richTextBox1.Select(0, 0);
// Replace the ending "}\r\n" with "\\par }\r\n". "\\par" is a line break.
richTextBox1.SelectedRtf =
rtfLines[i].Substring(0, rtfLines[i].Length - 3) + "\\par }\r\n";
}
The task seems not complicated(if I understand it correctly).
Get your clipboard into string then parse into array- use Split().
Then determine how many randon events you need and iterate through every word ; generate random number for each iteration(which should match the amount of events), intersect that number with one of the events and apply that case to that particular word. Maybe not the most efficient way to do it, but that's what comes to my mind

How to compare 2 strings with int out of errors?

I've searched online for a diff algorithm but none of them do what I am looking for. It is for a texting contest (as in cell phone) and I need the entry text compared to the master text recording the errors along the way. I am semi-new to C# and I get most of the string functions and didn't think this was going to be that hard of a problem, but alas I just can't wrap my head around it.
I have a form with 2 rich-text-boxes (one on top of the other) and 2 buttons. The top box is the master text (string) and the bottom box is the entry text (string). Every contestant is sending a text to an email account, from the email we copy and paste the text into the Entry RTB and compare to the Master RTB. For each single word and single space counts as a thing to check. A word, no matter how many errors it has, is still 1 error. And for every error add 1 sec. to their time.
Examples:
Hello there! <= 3 checks (2 words and 1 space)
Helothere! <= 2 errors (Helo and space)
Hello there!! <= 1 error (extra ! at end of there!)
Hello there! How are you? <= 9 checks (5 words and 4 spaces)
Helothere!! How a re you? <= still 9 checks, 4 errors(helo, no space, extra !, and a space in are)
Hello there!# Ho are yu?? <= 3 errors (# at end of there!, no w, no o and extra ? (all errors are still under the 1 word)
What I have so far:
I've created 6 arrays (3 for master, 3 for entry) and they are
CharArray of all chars
StringArray of all strings(words) including the spaces
IntArray with length of the string in each StringArray
My biggest trouble is if the entry text is wrong and it's shorter or longer than the master. I keep getting IndexOutOfRange exceptions (understandably) but can't fathom how to go about checking and writing the code to compensate.
I hope I have made myself clear enough as to what I need help with. If anyone could give some code examples or something to shoot me in the right path would be very helpful.
Have you looked into the Levenshtein distance algorithm? It returns the number of differences between two strings, which, in your case would be texting errors. Implementing the algorithm based off the pseudo-code found on the wikipedia page passes the first 3 of your 4 use cases:
Assert.AreEqual(2, LevenshteinDistance("Hello there!", "Helothere!");
Assert.AreEqual(1, LevenshteinDistance("Hello there!", "Hello there!!"));
Assert.AreEqual(4, LevenshteinDistance("Hello there! How are you?", "Helothere!! How a re you?"));
Assert.AreEqual(3, LevenshteinDistance("Hello there! How are you?", "Hello there!# Ho are yu??")); //fails, returns 4 errors
So while not perfect out of the box, it is probably a good starting point for you. Also, if you have too much trouble implementing your scoring rules, it might be worth revisiting them.
hth
Update:
Here is the result of the string you requested in the comments:
Assert.AreEqual(7, LevenshteinDistance("Hello there! How are you?", "Hlothere!! Hw a reYou?"); //fails, returns 8 errors
And here is my implementation of the Levenshtein Distance algorithm:
int LevenshteinDistance(string left, string right)
{
if (left == null || right == null)
{
return -1;
}
if (left.Length == 0)
{
return right.Length;
}
if (right.Length == 0)
{
return left.Length;
}
int[,] distance = new int[left.Length + 1, right.Length + 1];
for (int i = 0; i <= left.Length; i++)
{
distance[i, 0] = i;
}
for (int j = 0; j <= right.Length; j++)
{
distance[0, j] = j;
}
for (int i = 1; i <= left.Length; i++)
{
for (int j = 1; j <= right.Length; j++)
{
if (right[j - 1] == left[i - 1])
{
distance[i, j] = distance[i - 1, j - 1];
}
else
{
distance[i, j] = Min(distance[i - 1, j] + 1, //deletion
distance[i, j - 1] + 1, //insertion
distance[i - 1, j - 1] + 1); //substitution
}
}
}
return distance[left.Length, right.Length];
}
int Min(int val1, int val2, int val3)
{
return Math.Min(val1, Math.Min(val2, val3));
}
You need to come up with a scoring systems that works for you're situation.
I would make a word array after each space.
If a word is found on the same index +5.
If a word is found on the same index +-1 index location +3 (keep a counter how much words differ to increase the +- correction
If a needed word is found as part of another word +2
etc.etc. Matching words is hard, getting up with a rules engine that works is 'easier'
I once implemented an algorithm (which I can't find at the moment, I'll post code when I find it) which looked at the total number of PAIRS in the target string. i.e. "Hello, World!" would have 11 pairs, { "He", "el", "ll",...,"ld", "d!" }.
You then do the same thing on an input string such as "Helo World" so you have { "He",...,"ld" }.
You can then calculate accuracy as a function of correct pairs (i.e. input pairs that are in the list of target pairs), incorrect pairs (i.e. input pairs that do not exists in the list of target pairs), compared to the total list of target pairs. Over long enough sentences, this measure would be very accurate fair.
A simple algorithm would be to check letter by letter. If the letters differ increment the num of errors. If the next pairing of letters match, its a switched letter so just continue. If the messup matches the next letter, it is an omission and treat it accordingly. If the next letter matches the messed up one, its an insertion and treat it accordingly. Else the person really messed up and continue.
This doesn't get everything but with a few modifications this could become comprehensive.
a weak attempt at pseudocode:
edit: new idea. look at comments. I don't know the string functions off the top of my head so you'll have to figure that part out. The algorithm kinda fails for words that repeat a lot though...
string entry; //we'll pretend that this has stuff inside
string master; // this too...
string tempentry = entry; //stuff will be deleted so I need a copy to mess up
int e =0; //word index for entry
int m = 0; //word index for master
int errors = 0;
while(there are words in tempentry) //!tempentry.empty() ?
string mword = the next word in master;
m++;
int eplace = find mword in tempentry; //eplace is the index of where the mword starts in tempentry
if(eplace == -1) //word not there...
continue;
else
errors += m - e;
errors += find number of spaces before eplace
e = m // there is an error
tempentry = stripoff everything between the beginning and the next word// substring?
all words and spaces left in master are considered errors.
There are a couple of bounds checking errors that need to be fixed here but its a good start.

How do I get 3 lines of text from a paragraph

I'm trying to create an "snippet" from a paragraph. I have a long paragraph of text with a word hilighted in the middle. I want to get the line containing the word before that line and the line after that line.
I have the following piece of information:
The text (in a string)
The lines are deliminated by a NEWLINE character \n
I have the index into the string of the text I want to hilight
A couple other criteria:
If my word falls on first line of the paragraph, it should show the 1st 3 lines
If my word falls on the last line of the paragraph, it should show the last 3 lines
Should show the entire paragraph in the degenative cases (the paragraph only has 1 or 2 lines)
Here's an example:
This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph
Example, if my index points to BIRD, it should show lines 1, 2, & 3 as one complete string like this:
This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
If my index points to DOG, it should show lines 3, 4, & 5 as one complete string like this:
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph
etc.
Anybody want to help tackle this?
In my opinion this is an excellent opportunity to use the StringReader class:
Read your text line by line.
Keep your lines in some kind of buffer (e.g., a Queue<string>), dropping lines you don't need after a given number of lines have been read.
Once your "needle" is found, read one more line (if possible) and then just return what's in your buffer.
In my opinion, this has some advantages over the other approaches suggested:
Since it doesn't utilize String.Split, it doesn't do more work than you need -- i.e., reading the entire string looking for the characters to split on, and creating an array of the substrings.
In fact, it doesn't necessarily read the entire string at all, since once it finds the text it's looking for it only goes as far as necessary to get the desired number of padding lines.
It could even be refactored (very easily) to be able to deal with any textual input via a TextReader -- e.g., a StreamReader -- so it could even work with huge files, without having to load the entire contents of a given file into memory.
Imagine this scenario: you want to find an excerpt of text from a text file that contains the entire text from a novel. (Not that this is your scenario -- I'm just speaking hypothetically.) Using String.Split would require that the entire text of the novel be split according to the delimiter you specified, whereas using a StringReader (well, in this case, a StreamReader) would only require reading until the desired text was found, at which point the excerpt would be returned.
Again, I realize this isn't necessarily your scenario -- just suggesting that this approach provides scalability as one of its strengths.
Here's a quick implementation:
// rearranged code to avoid horizontal scrolling
public static string FindSurroundingLines
(string haystack, string needle, int paddingLines) {
if (string.IsNullOrEmpty(haystack))
throw new ArgumentException("haystack");
else if (string.IsNullOrEmpty(needle))
throw new ArgumentException("needle");
else if (paddingLines < 0)
throw new ArgumentOutOfRangeException("paddingLines");
// buffer needs to accomodate paddingLines on each side
// plus line containing the needle itself, so:
// (paddingLines * 2) + 1
int bufferSize = (paddingLines * 2) + 1;
var buffer = new Queue<string>(/*capacity*/ bufferSize);
using (var reader = new StringReader(haystack)) {
bool needleFound = false;
while (!needleFound && reader.Peek() != -1) {
string line = reader.ReadLine();
if (buffer.Count == bufferSize)
buffer.Dequeue();
buffer.Enqueue(line);
needleFound = line.Contains(needle);
}
// at this point either the needle has been found,
// or we've reached the end of the text (haystack);
// all that's left to do is make sure the string returned
// includes the specified number of padding lines
// on either side
int endingLinesRead = 0;
while (
(reader.Peek() != -1 && endingLinesRead++ < paddingLines) ||
(buffer.Count < bufferSize)
) {
if (buffer.Count == bufferSize)
buffer.Dequeue();
buffer.Enqueue(reader.ReadLine());
}
var resultBuilder = new StringBuilder();
while (buffer.Count > 0)
resultBuilder.AppendLine(buffer.Dequeue());
return resultBuilder.ToString();
}
}
Some example input/output (with text containing your example input):
Code:
Console.WriteLine(FindSurroundingLines(text, "MOUSE", 1);
Output:
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
Code:
Console.WriteLine(FindSurroundingLines(text, "BIRD", 1);
Output:
This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
Code:
Console.WriteLine(FindSurroundingLines(text, "DOG", 0);
Output:
This is the 4th line of DOG text in the paragraph
Code:
Console.WriteLine(FindSurroundingLines(text, "This", 2);
Output:
This is the 1st line of CAT text in the paragraph
This is the 2nd line of BIRD text in the paragraph
This is the 3rd line of MOUSE text in the paragraph
This is the 4th line of DOG text in the paragraph
This is the 5th line of RABBIT text in the paragraph
Using the LINQ extension methods to get the right strings:
string[] lines = text.Split('\n');
// Find the right line to work with
int position = 0;
for (int i = 0; i < lines.Count(); i++)
if (lines[i].Contains(args[0]))
position = i - 1;
// Get in range if we had a match in the first line
if (position == -1)
position = 0;
// Adjust the line index so we have 3 lines to work with
if (position > lines.Count() - 3)
position = lines.Count() - 3;
string result = String.Join("\n", lines.Skip(position).Take(3).ToArray());
This can of course be optimized a bit by quitting the for loop as soon as the index has been found, and probably a number of other things. You can probably even LINQify so you never need to actually store that extra array, but I can't think of a good way to do that right now.
An alternative for the checks on position could be something like position = Math.Max(0,Math.Min(position, lines.Count() - 3)); - which would handle both of them at once.
There are a few ways one can handle this:
First Method:
Use String.IndexOf() and String.LastIndexOf().
You can find where the current selected word is by using TextBox.SelectionStart(). Then simply look for LastIndexOf from the selection location looking for the '\n' to find the previous line (don't grab the first lastindexof from the selection, once you find one...do it again from that location so you get the beginning of that line). Then do the same from the selection point only using IndexOf to find the '\n' to get the end of the line. Once again, don't use the first one you find, repeat it starting from the first found location to get the second line's end. Then simply substring the text with the area you found.
Second Method: Use String.Split() by the '\n' character (creates an array of strings, each one containing a different line from the text in order of array index). Find the index of the line the text is in, and then simply grab from the String[index] for the line before, including, and after. Hopefully this two methods are clear enough for you to figure out your coding. If you are still stuck, let me know.
Alright. Lemme have a crack,
I think the first thing I would do is split everything into arrays. Simply because then we have a simple way to "count" the lines.
string[] lines = fullstring.Split('\n');
Once we have that, Unfortunately I don't know of any indexof that goes through each point in an array. There probably is one, but without trawling through the internet, I would simply go
int i = -1;
string animal = 'bird';
foreach(string line in lines)
{
i++;
if(line.indexof(animal) > -1) break;
}
// we will need a if(i == -1) then we didn't find the animal etc
Ok so then, We now have the line. All we need to do, is...
if(i == 0)
{
writeln(lines[0);
writeln(lines[1]);
etc
}
else
if(i == lines.count - 1)
{
//this means last array index
}
else
{
//else we are in the middle. So just write out the i -1, i, i+1
}
I know that is messy as hell. But that's how I would solve the issue.

Categories

Resources