Replace all text in a rich text box - c#

I have a problem while trying to replace all text matching a particular word in a rich text box. This is the code i use
public static void ReplaceAll(RichTextBox myRtb, string word, string replacer)
{
int index = 0;
while (index < myRtb.Text.LastIndexOf(word))
{
int location = myRtb.Find(word, index, RichTextBoxFinds.None);
myRtb.Select(location, word.Length);
myRtb.SelectedText = replacer;
index++;
}
MessageBox.Show(index.ToString());
}
private void btnReplaceAll_Click(object sender, EventArgs e)
{
Form1 text = (Form1)Application.OpenForms["Form1"];
ReplaceAll(text.Current, txtFind2.Text, txtReplace.Text);
}
This works well but i have noticed a little malfunction when i try to replace a letter with itself and another letter.
For example i want to replace all the e in Welcome to Nigeria with ea.
This is what i get Weaalcomeaaaaaaa to Nigeaaaaaaaaaaaaaaria.
And the message box shows 23 when there are only three e. Pls what am i doing wrong and how can i correct it

Simply do this:
yourRichTextBox.Text = yourRichTextBox.Text.Replace("e","ea");
If you want to report the number of matches (which are replaced), you can try using Regex like this:
MessageBox.Show(Regex.Matches(yourRichTextBox.Text, "e").Count.ToString());
UPDATE
Of course, using the method above has expensive cost in memory, you can use some loop in combination with Regex to achieve some kind of advanced replacing engine like this:
public void ReplaceAll(RichTextBox myRtb, string word, string replacement){
int i = 0;
int n = 0;
int a = replacement.Length - word.Length;
foreach(Match m in Regex.Matches(myRtb.Text, word)){
myRtb.Select(m.Index + i, word.Length);
i += a;
myRtb.SelectedText = replacement;
n++;
}
MessageBox.Show("Replaced " + n + " matches!");
}

Related

Method to draw a line

My Professor gave us this question
Write a method called Drawline that accepts as input an integer n and generates a line of output in lstOutput with n hyphens. That is, if n = 5 we have a line of ‘-----‘ displayed in the list box.
Basically he wants me to type a number in a text box and when i click the button it should display that many hyphens in a list box. Using visual Studio C# WindowsFormApp.
Here's my code:
private void btn3_Click(object sender, EventArgs e)
{
double n;
Drawline(out n);
}
private void Drawline(out double n)
{
n = double.Parse(textBox1.Text);
string strline = "";
for (n = 1; n <= 5; n++);
strline += '-';
lstOutput.Items.Add(String.Format(strline, n));
}
It works but no matter what number i put in the text box only one hyphen shows up. Can anyone help me?
The problem is with your for loop in DrawLine method.
You need to remove the semi-colon at the end of the for statement, so the strLine += '-'; will belong to the loop, not just be executed once.
private void Drawline(out double n)
{
n = double.Parse(textBox1.Text);
string strline = "";
for (i = 1; i <= 5; i++)
strline += '-';
lstOutput.Items.Add(String.Format(strline, n));
}
It appears you may be making this more complicated than it has to be.
It is unclear “why” the DrawLine method returns a double value using the out property? Is this a requirement? If it is not a requirement, then it is unnecessary.
Also, as per the requirement… ”Write a method called Drawline that accepts as input an integer n” … if this is the requirement, I have to ask why is the method accepting a double value? This would not fit with the requirement.
Below is a simplified version and should fit your requirements. First in the button click event, we want to get the integer value from the text box. We need to assume the user typed in a value that is NOT a valid integer. If the value is NOT a valid integer greater than zero (0), then we will display a message box indicating such.
private void button1_Click(object sender, EventArgs e) {
if ((int.TryParse(textBox1.Text, out int value)) && value > 0) {
Drawline(value);
}
else {
MessageBox.Show("String is not a number or is less than 1 : " + textBox1.Text);
}
}
Next the DrawLine method that simply adds a string of “-“ character(s) to the list box. Note the passed-in/accepted value of n has already been verified as a valid integer number greater than 0.
private void Drawline(int n) {
lstOutput.Items.Add(new string('-', n));
}
If you MUST use a for loop to generate the string, it may look something like…
private void Drawline(int n) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.Append("-");
}
lstOutput.Items.Add(sb.ToString());
}

C# How to generate a new string based on multiple ranged index

Let's say I have a string like this one, left part is a word, right part is a collection of indices (single or range) used to reference furigana (phonetics) for kanjis in my word:
string myString = "子で子にならぬ時鳥,0:こ;2:こ;7-8:ほととぎす"
The pattern in detail:
word,<startIndex>(-<endIndex>):<furigana>
What would be the best way to achieve something like this (with a space in front of the kanji to mark which part is linked to the [furigana]):
子[こ]で 子[こ]にならぬ 時鳥[ほととぎす]
Edit: (thanks for your comments guys)
Here is what I wrote so far:
static void Main(string[] args)
{
string myString = "ABCDEF,1:test;3:test2";
//Split Kanjis / Indices
string[] tokens = myString.Split(',');
//Extract furigana indices
string[] indices = tokens[1].Split(';');
//Dictionnary to store furigana indices
Dictionary<string, string> furiganaIndices = new Dictionary<string, string>();
//Collect
foreach (string index in indices)
{
string[] splitIndex = index.Split(':');
furiganaIndices.Add(splitIndex[0], splitIndex[1]);
}
//Processing
string result = tokens[0] + ",";
for (int i = 0; i < tokens[0].Length; i++)
{
string currentIndex = i.ToString();
if (furiganaIndices.ContainsKey(currentIndex)) //add [furigana]
{
string currentFurigana = furiganaIndices[currentIndex].ToString();
result = result + " " + tokens[0].ElementAt(i) + string.Format("[{0}]", currentFurigana);
}
else //nothing to add
{
result = result + tokens[0].ElementAt(i);
}
}
File.AppendAllText(#"D:\test.txt", result + Environment.NewLine);
}
Result:
ABCDEF,A B[test]C D[test2]EF
I struggle to find a way to process ranged indices:
string myString = "ABCDEF,1:test;2-3:test2";
Result : ABCDEF,A B[test] CD[test2]EF
I don't have anything against manually manipulating strings per se. But given that you seem to have a regular pattern describing the inputs, it seems to me that a solution that uses regex would be more maintainable and readable. So with that in mind, here's an example program that takes that approach:
class Program
{
private const string _kinvalidFormatException = "Invalid format for edit specification";
private static readonly Regex
regex1 = new Regex(#"(?<word>[^,]+),(?<edit>(?:\d+)(?:-(?:\d+))?:(?:[^;]+);?)+", RegexOptions.Compiled),
regex2 = new Regex(#"(?<start>\d+)(?:-(?<end>\d+))?:(?<furigana>[^;]+);?", RegexOptions.Compiled);
static void Main(string[] args)
{
string myString = "子で子にならぬ時鳥,0:こ;2:こ;7-8:ほととぎす";
string result = EditString(myString);
}
private static string EditString(string myString)
{
Match editsMatch = regex1.Match(myString);
if (!editsMatch.Success)
{
throw new ArgumentException(_kinvalidFormatException);
}
int ichCur = 0;
string input = editsMatch.Groups["word"].Value;
StringBuilder text = new StringBuilder();
foreach (Capture capture in editsMatch.Groups["edit"].Captures)
{
Match oneEditMatch = regex2.Match(capture.Value);
if (!oneEditMatch.Success)
{
throw new ArgumentException(_kinvalidFormatException);
}
int start, end;
if (!int.TryParse(oneEditMatch.Groups["start"].Value, out start))
{
throw new ArgumentException(_kinvalidFormatException);
}
Group endGroup = oneEditMatch.Groups["end"];
if (endGroup.Success)
{
if (!int.TryParse(endGroup.Value, out end))
{
throw new ArgumentException(_kinvalidFormatException);
}
}
else
{
end = start;
}
text.Append(input.Substring(ichCur, start - ichCur));
if (text.Length > 0)
{
text.Append(' ');
}
ichCur = end + 1;
text.Append(input.Substring(start, ichCur - start));
text.Append(string.Format("[{0}]", oneEditMatch.Groups["furigana"]));
}
if (ichCur < input.Length)
{
text.Append(input.Substring(ichCur));
}
return text.ToString();
}
}
Notes:
This implementation assumes that the edit specifications will be listed in order and won't overlap. It makes no attempt to validate that part of the input; depending on where you are getting your input from you may want to add that. If it's valid for the specifications to be listed out of order, you can also extend the above to first store the edits in a list and sort the list by the start index before actually editing the string. (In similar fashion to the way the other proposed answer works; though, why they are using a dictionary instead of a simple list to store the individual edits, I have no idea…that seems arbitrarily complicated to me.)
I included basic input validation, throwing exceptions where failures occur in the pattern matching. A more user-friendly implementation would add more specific information to each exception, describing what part of the input actually was invalid.
The Regex class actually has a Replace() method, which allows for complete customization. The above could have been implemented that way, using Replace() and a MatchEvaluator to provide the replacement text, instead of just appending text to a StringBuilder. Which way to do it is mostly a matter of preference, though the MatchEvaluator might be preferred if you have a need for more flexible implementation options (i.e. if the exact format of the result can vary).
If you do choose to use the other proposed answer, I strongly recommend you use StringBuilder instead of simply concatenating onto the results variable. For short strings it won't matter much, but you should get into the habit of always using StringBuilder when you have a loop that is incrementally adding onto a string value, because for long string the performance implications of using concatenation can be very negative.
This should do it (and even handle ranged indices), based on the formatting of the input string you have-
using System;
using System.Collections.Generic;
public class stringParser
{
private struct IndexElements
{
public int start;
public int end;
public string value;
}
public static void Main()
{
//input string
string myString = "子で子にならぬ時鳥,0:こ;2:こ;7-8:ほととぎす";
int wordIndexSplit = myString.IndexOf(',');
string word = myString.Substring(0,wordIndexSplit);
string indices = myString.Substring(wordIndexSplit + 1);
string[] eachIndex = indices.Split(';');
Dictionary<int,IndexElements> index = new Dictionary<int,IndexElements>();
string[] elements;
IndexElements e;
int dash;
int n = 0;
int last = -1;
string results = "";
foreach (string s in eachIndex)
{
e = new IndexElements();
elements = s.Split(':');
if (elements[0].Contains("-"))
{
dash = elements[0].IndexOf('-');
e.start = int.Parse(elements[0].Substring(0,dash));
e.end = int.Parse(elements[0].Substring(dash + 1));
}
else
{
e.start = int.Parse(elements[0]);
e.end = e.start;
}
e.value = elements[1];
index.Add(n,e);
n++;
}
//this is the part that takes the "setup" from the parts above and forms the result string
//loop through each of the "indices" parsed above
for (int i = 0; i < index.Count; i++)
{
//if this is the first iteration through the loop, and the first "index" does not start
//at position 0, add the beginning characters before its start
if (last == -1 && index[i].start > 0)
{
results += word.Substring(0,index[i].start);
}
//if this is not the first iteration through the loop, and the previous iteration did
//not stop at the position directly before the start of the current iteration, add
//the intermediary chracters
else if (last != -1 && last + 1 != index[i].start)
{
results += word.Substring(last + 1,index[i].start - (last + 1));
}
//add the space before the "index" match, the actual match, and then the formatted "index"
results += " " + word.Substring(index[i].start,(index[i].end - index[i].start) + 1)
+ "[" + index[i].value + "]";
//remember the position of the ending for the next iteration
last = index[i].end;
}
//if the last "index" did not stop at the end of the input string, add the remaining characters
if (index[index.Keys.Count - 1].end + 1 < word.Length)
{
results += word.Substring(index[index.Keys.Count-1].end + 1);
}
//trimming spaces that may be left behind
results = results.Trim();
Console.WriteLine("INPUT - " + myString);
Console.WriteLine("OUTPUT - " + results);
Console.Read();
}
}
input - 子で子にならぬ時鳥,0:こ;2:こ;7-8:ほととぎす
output - 子[こ]で 子[こ]にならぬ 時鳥[ほととぎす]
Note that this should also work with characters the English alphabet if you wanted to use English instead-
input - iliketocodeverymuch,2:A;4-6:B;9-12:CDEFG
output - il i[A]k eto[B]co deve[CDEFG]rymuch

clickable text in richtextbox

I want to make a clickable text through code in c# (its not a URL just ordinary text) so when i click that text it do linkclicked event.
It is for a dictionary so when I search something and in the description there is a word same from the storage I can click it to search that word.
for (int j = 0; j <= jml[i]; j++)
{
richTextBox4.AppendText(j + 1 + ". ");
string[] desk = sk[i, j].Split(' ');
for (int l = 0; l < desk.Count(); l++)
{
for (int m = 0; m < kata.Count(); m++)
{
if (desk[l] == kata[m])
{
richTextBox4.SelectionColor = Color.Blue;
desk[l] = LinkArea;
}
}
richTextBox4.AppendText(desk[l] + " ");
richTextBox4.SelectionColor = Color.Black;
}
richTextBox4.AppendText("\n");
}
my code for the moment(for the hyperlink type text just the color that work)
There is no built-in way but if you know what you want it is not hard to build.
Step one is to decide just how the clickable text is defined, read: What are the separators?
If you want single words it is rather easy. One way is using a regEx to identify words in a RTB. If you don't like the built-in way to define a word, I can post a code with user-defined separators.. But maybe you are happy with what have already.
If you need whole phrases (including spaces) to be clickable as one whole entity then you will have to be a little more inventive:
either you can add special characters around the phrases
or you can replace the spaces by non-breakable spaces
Or you could write a complete map into your text, but that probably defies your purpose.
Anything but simple words will mean that you will need to apply some changes to your text.
The result should be a function that can find the start and the end of the clickable text and return the clicked word/phrase.
Step Two is to look up the text in a Dictionary<string, string> and do what you want to do with the value, e.g. show the translation in a Label:
if (dictEnGE.ContainsKey(theClickedWord))
label.Text = dictEnGE[theClickedWord];
For this to work you must first prepare the dictionary by adding the key-value pairs: The keys are the clickable texts and the values are whatever you need to find:
Dictionary<string, string> dictEnGE = new Dictionary<string, string>();
dictEnGE.Add("house", "Haus");
dictEnGE.Add("man", "Mann");
dictEnGE.Add("mouse", "Maus");
//..
So the whole solution is: Prepare a dictionary, code the Mouseclick event to find the clicked text, look up the text in the dictioanry, do your thing with the value you find..
Update: Here is a function to get the clicked word:
string getWordAtIndex(RichTextBox RTB, int index)
{
string wordSeparators = " .,;-!?\r\n\"";
int cp0 = index;
int cp2 = RTB.Find(wordSeparators.ToCharArray(), index);
for (int c = index; c > 0; c--)
{ if (wordSeparators.Contains(RTB.Text[c])) { cp0 = c + 1; break; } }
int l = cp2 - cp0;
if (l > 0) return RTB.Text.Substring(cp0, l); else return "";
}
and here is how you could use it:
private void richTextBox1_MouseClick(object sender, MouseEventArgs e)
{
string word = getWordAtIndex(richTextBox1, richTextBox1.SelectionStart);
if (dictEnGE.ContainsKey(word)) aLabel.Text = dictEnGE[word];
}

search and highlight text in textbox

im trying to make some kind of search function in my program which is just highlighting all keywords found on textbox
the keyword box is on t2.text and the text is from bx2.text
firstly i tried out this method from here Highlight key words in a given search text
the regex is working but the highlight not
any wrong here?
private void searchs()
{
var spattern = t2.Text;
foreach(var s in bx2.Text)
{
var zxc = s.ToString();
if (System.Text.RegularExpressions.Regex.IsMatch(zxc, spattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase))
{
bx2.Text.Replace(bx2.Text, #"<span class=""search-highlight"">$0</span>");
}
}
}
The linked question uses HTML formatting, which you can't use in a regular textbox.
You need either a RichText box. In there you can format text using the control's methods and properties (not using HTML).
Or use a browser control rather than a textbox to use the HTML formatting.
private void findButton_Click(object sender, EventArgs e)
{
int count = 0;
string keyword = keywordTextbox.Text.Trim();//getting given searching string
int startPosition = 0; //initializing starting position to search
int endPosition = 0;
int endArticle = articleRichTextbox.Text.Length;//finding total number of character into the article
for (int i = 0; i<endArticle ; i =startPosition )//creating a loop until ending the article
{
if (i == -1) //if the loop goes to end of the article then stop
{
break;
}
startPosition = articleRichTextbox.Find(keyword, startPosition, endArticle, RichTextBoxFinds.WholeWord);//using find method get the begining position when find the searching string
if (startPosition >= 0) //if match the string //if don't match the string then it return -1
{
count++;//conunting the number of occuerence or match the search string
articleRichTextbox.SelectionColor = Color.Blue;//coloring the matching string in the article
endPosition = keywordTextbox.Text.Length;
startPosition = startPosition + endPosition;//place the starting position at the next word of previously matching string to continue searching.
}
}
if (count == 0)//if the givn search string don't match at any time
{
MessageBox.Show("No Match Found!!!");
}
numberofaccurTextbox.Text = count.ToString();//show the number of occurence into the text box
}

How to color some words in differents lines in the RichTextBox?

I'm in trouble, today I tried to color some diffents words in differents lines clicked on a button. Can You explain me how to do this? I was able to do only this:
private void button1_Click(object sender, EventArgs e)
{
richTextBox1.Select(int start , int length); //It's wrong but It explains How to use .Select if you know start and length...
richTextBox1.SelectionColor = Color.Blue;
}
But how can I work on a line I know the number of, having already the text into the RichTextBox?
Thanks.
If lines are separated by \n, your problem is then to count the number of \n characters before getting to the wanted line.
For that, you can use an extension method:
public static int NthIndexOf(this String str, String match, int occurence) {
int i = 1;
int index = 0;
while (i <= occurence && ( index = str.IndexOf(match, index + 1) ) != -1) {
if (i == occurence) {
// Occurence match found!
return index;
}
i++;
}
// Match not found
return -1;
}
Now, you can find start and end values to color the selection:
private void button1_Click(object sender, EventArgs e) {
int lineNb = 13; // I assume you get this value initialized somewhere,
// I wrote 13 for the example
int start = richTextBox1.Text.NthIndexOf("\n", lineNb);
int length = richTextBox1.Text.NthIndexOf("\n", lineNb + 1);
richTextBox1.Select(start , length);
richTextBox1.SelectionColor = Color.Blue;
}

Categories