RichTextBox - How highlight end of line? - c#

If the searched word is at the beginning or end of the line of, I want to color it. If it's in the middle of the line not colorful. I tried many thing but it's not working right.
Seems like beginning of the line is working. But end of the first line only can color. I want to color for all lines end. I think i need index of each line's beggining and loop but i can't do it.
How can i fix it?
private void button1_Click(object sender, EventArgs e)
{
int wordLength = textBox1.Text.Length;
string word = textBox1.Text;
for (int i = 0; i < richTextBox1.Lines.Count(); i++)
{
int startIndex = richTextBox1.GetFirstCharIndexFromLine(i);
richTextBox1.Find(word, startIndex, startIndex+wordLength, RichTextBoxFinds.None);
richTextBox1.SelectionColor = Color.Red;
richTextBox1.SelectionBackColor = Color.Yellow;
int newLineIndex = richTextBox1.Lines[i].Length;
richTextBox1.Find(textBox1.Text, (newLineIndex - wordLength), newLineIndex, RichTextBoxFinds.None);
richTextBox1.SelectionColor = Color.Red;
richTextBox1.SelectionBackColor = Color.Yellow;
}

Try:
int newLineIndex = i + 1 < richTextBox1.Lines.Length ? richTextBox1.GetFirstCharIndexFromLine(i + 1) - 1 : richTextBox1.TextLength;

I suggest to change your code a little bit. You'll notice why when the RichTextBox text length grows.
Asking for the Lines[] content is not exactly a good thing, much worse in a loop, when you access this Property probably many of times.
You can see in the .Net Source Code what happens (each time - the Lines Property values are not cached and cannot be).
GetLineFromCharIndex() and GetFirstCharIndexFromLine() use instead SendMessage to send the EM_LINEFROMCHAR and EM_LINEINDEX messages to the Edit control - which uses cached values - and are pretty fast.
Use Regex.Matches() to collect the indexes of the matched word(s) (you can use more than one word, separated by a pipe: "|", but here we handle just one word. When matching more than one word, use a List<Match> and the Match.Length instead of searchWord.Length) and extract just the Index position of each match.
Then, loop the indexes and check whether the current index position meets the criteria.
The current line end is found with IndexOf("\n", [StartPosition]), using the first line index (which is also used for the selection) as the starting position.
The RichTextBox Control uses only \n as line separator, so we don't need to worry about \r.
string searchWord = "John";
var txt = richTextBox1.Text;
int textLenght = txt.Length;
// the indexes list can be created with the alternative method (using IndexOf() in a loop)
var indexes = Regex.Matches(txt, searchWord, RegexOptions.Multiline)
.OfType<Match>()
.Select(m => m.Index).ToList();
foreach (int index in indexes) {
int currentLine = richTextBox1.GetLineFromCharIndex(index);
int lineFirstIndex = richTextBox1.GetFirstCharIndexFromLine(currentLine);
int lineLastIndex = txt.IndexOf("\n", lineFirstIndex);
if (index == lineFirstIndex ||
index == lineLastIndex - searchWord.Length ||
index == textLenght - searchWord.Length) {
richTextBox1.Select(index, searchWord.Length);
richTextBox1.SelectionColor = Color.Red;
}
}
Edit: Since Regex.Matches is not allowed, you can use IndexOf() in a loop:
var indexes = new List<int>();
int wordPosition = -1;
do {
if ((wordPosition = txt.IndexOf(searchWord, wordPosition + 1)) >= 0) {
indexes.Add(wordPosition);
}
} while (wordPosition >= 0);

Related

Im trying to use indexof and substring to extract text from a file but the variable index is -1 all the time what is wrong?

I have a html file with some strings inside for example:
"http://www.niederschlagsradar.de/images.aspx?jaar=-6&type=europa.precip&datum=201309150000&cultuur=en-GB&continent=europa","http://www.niederschlagsradar.de/images.aspx?jaar=-6&type=europa.precip&datum=201309150300&cultuur=en-GB&continent=europa","http://www.niederschlagsradar.de/images.aspx?jaar=-6&type=europa.precip&datum=201309150600&cultuur=en-GB&continent=europa"
I ant to extract each line: http://www.niederschlagsradar.de/images.aspx?jaar=-6&type=europa.precip&datum=201309150000&cultuur=en-GB&continent=europa
Then the next one: http://www.niederschlagsradar.de/images.aspx?jaar=-6&type=europa.precip&datum=201309150300&cultuur=en-GB&continent=europa
This is the code im using:
In the constructor i did:
f = File.ReadAllText(localFilename + "test.html");
retrivingText1();
private void retrivingText1()
{
string startTag = "http://www.niederschlagsradar.de/images.aspx";//"<Translation>";
string endTag = "continent=europa";//"</Translation>";
int startTagWidth = startTag.Length;
int endTagWidth = endTag.Length;
index = 0;
w = new StreamWriter(#"d:\retrivedText1.txt");
while (true)
{
index = f.IndexOf(startTag, index);
if (index == -1)
{
break;
}
// else more to do - index now is positioned at first character of startTag
int start = index + startTagWidth;
index = f.LastIndexOf(endTag, start + 1);
if (index == -1)
{
break;
}
// found the endTag
string g = f.Substring(start, index - start + endTagWidth).Trim(); //Trim the founded text so the start and ending spaces are removed.
w.WriteLine(g);
//break so you dont have an endless loop
break;
}
w.Close();
}
I know for extracting from html file its better to use htmlagilitypack or regex. But i wanted to try this time indexof and substring.
When i use a breakpoint so on the line:
int start = index + startTagWidth;
start = 2950
The next line after it index = -1
I prefer Don's answer, but if you really want to use indexof it is much easier if you prime the loop and do something like this:
private void button3_Click(object sender, EventArgs e)
{
string f = "\"http://www.niederschlagsradar.de/images.aspx?jaar=-6&type=europa.precip&datum=201309150000&cultuur=en-GB&continent=europa\",\"http://www.niederschlagsradar.de/images.aspx?jaar=-6&type=europa.precip&datum=201309150300&cultuur=en-GB&continent=europa\",\"http://www.niederschlagsradar.de/images.aspx?jaar=-6&type=europa.precip&datum=201309150600&cultuur=en-GB&continent=europa\"";
int startIndex = 0;
int endIndex = 0;
int position = 0;
string startTag = "http://www.niederschlagsradar.de/images.aspx";//"<Translation>";
string endTag = "continent=europa";//"</Translation>";
startIndex = f.IndexOf(startTag);
while (startIndex > 0)
{
endIndex = f.IndexOf(endTag, position);
//parse out what you want
position = endIndex + endTag.Length;
startIndex = (f.IndexOf(startTag, position));
//something here to prevent endless loop
}
}
On the Page you refer to I cannot find the lines of text that you are looking for...
I think, like you also thought about, that it would be alot better using a regular expression:
http:\/\/www\.niederschlagsradar\.de\/images\.aspx\?jaar=-6&type=europa\.precip&datum=\d{12}&cultuur=en-GB&continent=europa
Then you will get all the references you need for further processing.
EDIT
If you wan't to use IndexOf and SubString. You are using the LastIndexOf in the wrong way. LastIndexOf is seaching backwards in the string toward the beginning of the string.
Documentation
Try to just use IndexOf instead
Given your example file, I would prefer:
String[] sa = f.Split(',');
foreach (String s in sa)
{
String strToWrite = f.Trim('\"');
//write your string
}

c# error : length cannot be less than 0

I have a problem with my C# program: I have created a quiz with 10 questions and 10 images.
I get this Length cannot be less than zero.\r\nParameter name: length at the line
int imageIndex = int.Parse(line.Substring(0, delimiter));
Even if in my notepad file I included the image index:
3:What is the foo in the bar?
10:How can you add a widget?
4:Why pick a bar over a foo?
Here is the code:
if (nr >= questions.Count)
{
button1.Enabled = false;
}
else
{
Random r = new Random();
int x;
do
{
x = r.Next(questions.Count);
}
while (questions[x].displayed == true);
textBox1.Text = questionText;
radioButton1.Text = questions[x].answer1;
radioButton2.Text = questions[x].answer2;
questions[x].displayed= true;
current_question = x;
}
You've previously got a line like this:
int delimiter = line.IndexOf(':');
... but you're then not checking the return value. If it's -1, that means your delimiter wasn't found in that particular line - but you're passing it into Substring anyway. Check the value of delimiter before you use it - that way you can throw a more useful exception (or skip the line, or whatever you want to do).
I would actually suggest that you change your code significantly - instead of keeping questions as a List<string> or whatever it is, I'd create a Question class. Parse the lines of text as you read them, discarding failures - or throwing an exception - at that point, rather than waiting until you happen to hit the bad question. You can then have a List<Question> which will make the rest of the code simpler.
You might also want to keep a Queue<Question> which is initially a copy of the complete list, shuffled. When you want to show a new question, just take the next element from that queue. That way you won't need to loop round while you pick already-shown questions. (You'll want to include an Index or QuestionNumber property within the Question class, presumably...)
Note that it's possible that it's working for all the lines that you're really aware of, but that you've got some empty lines at the end of your file. You may well just want to skip empty lines.
Substring parameters are initial index, and length. delimiter from the code does not looks like length.
http://msdn.microsoft.com/en-us/library/aka44szs.aspx
Change following
int delimiter = line.IndexOf(':');
int imageIndex = int.Parse(line.Substring(0, delimiter));
string questionText=line.Substring(delimiter + 1);
pictureBox1.Image = imageList1.Images[imageIndex];
textBox1.Text = questionText;
radioButton1.Text = questions[x].answer1;
questions[x].displayed= true;
current_question = x;
To
int delimiter = line.IndexOf(':');
if(!String.IsNullOrEmpty(line) && delimiter > 0 )
{
int imageIndex = int.Parse(line.Substring(0, delimiter));
string questionText=line.Substring(delimiter + 1);
pictureBox1.Image = imageList1.Images[imageIndex];
textBox1.Text = questionText;
radioButton1.Text = questions[x].answer1;
questions[x].displayed= true;
current_question = x;
}
private string copystring(string instr ,int start ,int length)
{
return instr.Length >= (start + 1)
? (instr.Length > (start + length) ? instr.Substring(start, length) : instr.Substring(start,instr.Length-start))
: "";
}

C#/WPF: How to find the closest line breaks from the selection of a TextBox

I have a TextBox txtEditor. I want to find the nearest line breaks and select it
Example 1: With "no selection"
suppose the selection/cursor is *
this is the 1st line of text
this is *the 2nd line of text
this is the 3rd line of text
I want to extend the selection such that the selection is now
this is the 1st line of text
*this is the 2nd line of text*
this is the 3rd line of text
Example 2: With selection
this is the 1st line of text
this is *the 2nd line* of text
this is the 3rd line of text
I want to extend the selection such that the selection is now
this is the 1st line of text
*this is the 2nd line of text*
this is the 3rd line of text
Update: Possible solution
I found a possible solution, wonder if anyone got a better solution?
string tmp = txtEditor.Text;
int selStart = txtEditor.SelectionStart;
int selLength = txtEditor.SelectionLength;
int newSelStart;
int newSelLength;
string tmp1 = tmp.Substring(0, selStart);
if (tmp1.LastIndexOf(Environment.NewLine) < 0)
{
newSelStart = 0;
}
else
{
newSelStart = tmp1.LastIndexOf(Environment.NewLine) + Environment.NewLine.Length;
}
tmp1 = tmp.Substring(selStart);
if (tmp1.IndexOf(Environment.NewLine) < 0)
{
newSelLength = tmp.Length;
}
else
{
newSelLength = tmp1.IndexOf(Environment.NewLine) + selStart - newSelStart;
}
txtEditor.SelectionStart = newSelStart;
txtEditor.SelectionLength = newSelLength;
Well, mostly the problem is that your code is somwhat bloated (and thus harder to read) and a lot less efficient than it needs to be. Performance probably doesn't really matter in your case (those duplicate calls to IndexOf and LastIndexOf rub me the wrong way), but personally I'd rewrite your code like this:
string tmp = txtEditor.Text;
int selStart = txtEditor.SelectionStart;
int selLength = txtEditor.SelectionLength;
int newSelStart = tmp.LastIndexOf(Environment.NewLine, selStart);
if (newSelStart == -1)
newSelStart = 0;
int newSelEnd = tmp.IndexOf(Environment.NewLine, selStart);
if (newSelEnd == -1)
newSelEnd = tmp.Length;
txtEditor.SelectionStart = newSelStart;
txtEditor.SelectionLength = newSelEnd - newSelStart;

c# getting a string within another string

i have a string like this:
some_string = "A simple demo of SMS text messaging.\r\n+CMGW: 3216\r\n\r\nOK\r\n\"
im coming from vb.net and i need to know in c#, if i know the position of CMGW, how do i get "3216" out of there?
i know that my start should be the position of CMGW + 6, but how do i make it stop as soon as it finds "\r" ??
again, my end result should be 3216
thank you!
Find the index of \r from the start of where you're interested in, and use the Substring overload which takes a length:
// Production code: add validation here.
// (Check for each index being -1, meaning "not found")
int cmgwIndex = text.IndexOf("CMGW: ");
// Just a helper variable; makes the code below slightly prettier
int startIndex = cmgwIndex + 6;
int crIndex = text.IndexOf("\r", startIndex);
string middlePart = text.Substring(startIndex, crIndex - startIndex);
If you know the position of 3216 then you can just do the following
string inner = some_string.SubString(positionOfCmgw+6,4);
This code will take the substring of some_string starting at the given position and only taking 4 characters.
If you want to be more general you could do the following
int start = positionOfCmgw+6;
int endIndex = some_string.IndexOf('\r', start);
int length = endIndex - start;
string inner = some_string.SubString(start, length);
One option would be to start from your known index and read characters until you hit a non-numeric value. Not the most robust solution, but it will work if you know your input's always going to look like this (i.e., no decimal points or other non-numeric characters within the numeric part of the string).
Something like this:
public static int GetNumberAtIndex(this string text, int index)
{
if (index < 0 || index >= text.Length)
throw new ArgumentOutOfRangeException("index");
var sb = new StringBuilder();
for (int i = index; i < text.Length; ++i)
{
char c = text[i];
if (!char.IsDigit(c))
break;
sb.Append(c);
}
if (sb.Length > 0)
return int.Parse(sb.ToString());
else
throw new ArgumentException("Unable to read number at the specified index.");
}
Usage in your case would look like:
string some_string = #"A simple demo of SMS text messaging.\r\n+CMGW: 3216\r\n...";
int index = some_string.IndexOf("CMGW") + 6;
int value = some_string.GetNumberAtIndex(index);
Console.WriteLine(value);
Output:
3216
If you're looking to extract the number portion of 'CMGW: 3216' then a more reliable method would be to use regular expressions. That way you can look for the entire pattern, and not just the header.
var some_string = "A simple demo of SMS text messaging.\r\n+CMGW: 3216\r\n\r\nOK\r\n";
var match = Regex.Match(some_string, #"CMGW\: (?<number>[0-9]+)", RegexOptions.Multiline);
var number = match.Groups["number"].Value;
More general, if you don't know the start position of CMGW but the structure remains as before.
String s;
char[] separators = {'\r'};
var parts = s.Split(separators);
parts.Where(part => part.Contains("CMGW")).Single().Reverse().TakeWhile(c => c != ' ').Reverse();

What is the most efficient way to detect if a string contains a number of consecutive duplicate characters in C#?

For example, a user entered "I love this post!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
the consecutive duplicate exclamation mark "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" should be detected.
The following regular expression would detect repeating chars. You could up the number or limit this to specific characters to make it more robust.
int threshold = 3;
string stringToMatch = "thisstringrepeatsss";
string pattern = "(\\d)\\" + threshold + " + ";
Regex r = new Regex(pattern);
Match m = r.Match(stringToMatch);
while(m.Success)
{
Console.WriteLine("character passes threshold " + m.ToString());
m = m.NextMatch();
}
Here's and example of a function that searches for a sequence of consecutive chars of a specified length and also ignores white space characters:
public static bool HasConsecutiveChars(string source, int sequenceLength)
{
if (string.IsNullOrEmpty(source))
return false;
if (source.Length == 1)
return false;
int charCount = 1;
for (int i = 0; i < source.Length - 1; i++)
{
char c = source[i];
if (Char.IsWhiteSpace(c))
continue;
if (c == source[i+1])
{
charCount++;
if (charCount >= sequenceLength)
return true;
}
else
charCount = 1;
}
return false;
}
Edit fixed range bug :/
Can be done in O(n) easily: for each character, if the previous character is the same as the current, increment a temporary count. If it's different, reset your temporary count. At each step, update your global if needed.
For abbccc you get:
a => temp = 1, global = 1
b => temp = 1, global = 1
b => temp = 2, global = 2
c => temp = 1, global = 2
c => temp = 2, global = 2
c => temp = 3, global = 3
=> c appears three times. Extend it to get the position, then you should be able to print the "ccc" substring.
You can extend this to give you the starting position fairly easily, I'll leave that to you.
Here is a quick solution I crafted with some extra duplicates thrown in for good measure. As others pointed out in the comments, some duplicates are going to be completely legitimate, so you may want to narrow your criteria to punctuation instead of mere characters.
string input = "I loove this post!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!aa";
int index = -1;
int count =1;
List<string> dupes = new List<string>();
for (int i = 0; i < input.Length-1; i++)
{
if (input[i] == input[i + 1])
{
if (index == -1)
index = i;
count++;
}
else if (index > -1)
{
dupes.Add(input.Substring(index, count));
index = -1;
count = 1;
}
}
if (index > -1)
{
dupes.Add(input.Substring(index, count));
}
The better way i my opinion is create a array, each element in array is responsible for one character pair on string next to each other, eg first aa, bb, cc, dd. This array construct with 0 on each element.
Solve of this problem is a for on this string and update array values.
You can next analyze this array for what you want.
Example: For string: bbaaaccccdab, your result array would be { 2, 1, 3 }, because 'aa' can find 2 times, 'bb' can find one time (at start of string), 'cc' can find three times.
Why 'cc' three times? Because 'cc'cc & c'cc'c & cc'cc'.
Use LINQ! (For everything, not just this)
string test = "aabb";
return test.Where((item, index) => index > 0 && item.Equals(test.ElementAt(index)));
// returns "abb", where each of these items has the previous letter before it
OR
string test = "aabb";
return test.Where((item, index) => index > 0 && item.Equals(test.ElementAt(index))).Any();
// returns true

Categories

Resources