I need to highlight all words in richtextBox that are listed in .xls file, here's a part of my code:
public void HighlightWords(RichTextBox rtb1, DataTable dtXLS)
{
for (int i = 0; i < dtXLS.Rows.Count; i++)
{
string[] wordsToRedact = new string[dtXLS.Rows.Count];
wordsToRedact[i] = dtXLS.Rows[i][0].ToString();
Regex test = new Regex(#"[\p{P}|\s](" + wordsToRedact[i] + #")[\p{P}|\s]", RegexOptions.Singleline | RegexOptions.Compiled);
MatchCollection matchlist = test.Matches(rtb1.Text);
if (matchlist.Count > 0)
{
for (int j = 0; j < matchlist.Count; j++)
{
WordsToRedact words = new WordsToRedact(matchlist[j]);
HighLighting highLight = new HighLighting();
highLight.Highlight_Words(rtb1, words);
}
}
}
}
class HighLighting
{
public void Highlight_Words(RichTextBox rtb, WordsToRedact e)
{
rtb.SelectionBackColor = Color.LightSkyBlue;
rtb.SelectionStart = e.index;
rtb.SelectionLength = e.length;
rtb.ScrollToCaret();
}
}
class WordsToRedact
{
public int index;
public int length;
public string value;
public WordsToRedact(Match m)
{
this.index = m.Groups[1].Index;
this.length = m.Groups[1].Length;
this.value = m.Groups[1].Value;
}
}
The problem is, it didn't highlight some of the words that also matches the regex. Some are highlighted but some are not. Accuracy is my problem, and I don't know where I am getting wrong.
I checked your code, there were some problems in it, i list them below :
first :
for (int i = 0; i < dtXLS.Rows.Count; i++)
{
string[] wordsToRedact = new string[dtXLS.Rows.Count];
...
is wrong, you should initialize your string array before the for loop otherwise it gets renewed in each loop iteration, do this :
string[] wordsToRedact = new string[listBox1.Items.Count];
for (int i = 0; i < dtXLS.Rows.Count; i++)
{
...
second : (your major problem)
you should color the selected part after it is selected not before, that is why your code does not select the last match, you should do this :
rtb.SelectionStart = e.index;
rtb.SelectionLength = e.length;
rtb.SelectionBackColor = Color.LightSkyBlue;
and Last : (with doubt)
I think but I am not sure that you should use index zero [0] not [1]
public WordsToRedact(Match m)
{
this.index = m.Groups[0].Index;
this.length = m.Groups[0].Length;
this.value = m.Groups[0].Value;
}
This will work:
Regex test = new Regex(#"\b(" + wordsToRedact[i] + #")\b",
RegexOptions.Singleline | RegexOptions.Compiled);
mahdi-tahsildari's answer did answer my problem! But in addition with his answer I want also to post the other option that I've tried that also fixed the issue.
I changed the HighLighting class to this codes:
class HighLighting
{
public void HighlightText(RichTextBox rtb, string word)
{
int s_start = rtb.SelectionStart, startIndex = 0, index;
while ((index = rtb.Text.IndexOf(word, startIndex)) != -1)
{
rtb.Select(index, word.Length);
rtb.SelectionBackColor = Color.Yellow;
startIndex = index + word.Length;
}
rtb.SelectionStart = s_start;
rtb.SelectionLength = 0;
rtb.SelectionColor = Color.Black;
rtb.ScrollToCaret();
}
}
and everything goes fine. this code and mahdi-tahsildari's answer did the same thing! Thanks for all your help! :))
Related
Basically I want to make an autocorrect for my language. I have a RichTextBox(name: MainText), where I write. during writing, the program should every word if it exists in a dictionary file. if not then change the specific word color to red.
It has a timer. after every second it gets the written text and puts the words to an str array, and reads correct words from dictionary.txt file and puts them in a list. during comparison of the strings, it never highlights the incorrect words and it has always indexOutOfRange errors. How to fix it?
Here is the timer tick void:
void CheckTimer_Tick(object sender, EventArgs e)
{
List<string> correct_words = File.ReadAllLines(DictPath).ToList();
string text = MainText.Text;
string[] Words = text.Split(' ', '.');
for (int i = 0; i < Words.Length; i++)
{
if (correct_words.Contains(Words[i])) { }
else
{
int index = 0;
String temp = MainText.Text;
MainText.Text = "";
MainText.Text = temp;
while (index < MainText.Text.LastIndexOf(Words[i]))
{
MainText.Find(Words[i], index, MainText.TextLength, RichTextBoxFinds.None);
MainText.SelectionColor = Color.Red;
index = MainText.Text.IndexOf(Words[i], index) + 1;
}
}
}
}
I also tried this void:
void HighlightPhrase(RichTextBox box, string phrase, Color color)
{
int pos = box.SelectionStart;
string s = box.Text;
for (int ix = 0; ;)
{
int jx = s.IndexOf(phrase, ix, StringComparison.CurrentCultureIgnoreCase);
if (jx < 0) break;
box.SelectionStart = jx;
box.SelectionLength = phrase.Length;
box.SelectionColor = color;
ix = jx + 1;
}
box.SelectionStart = pos;
box.SelectionLength = 0;
box.ForeColor = Color.Black;
}
This worked if I gave it a specific string, but it couldn't recognize the phrase input from the dictionary and gave the indexOutOfRange error.
public Form1()
{
InitializeComponent();
}
private void textbox1_TextChanging(object sender, EventArgs e)
{
string[] words = textBox1.Text.Split(',');
foreach(string word in words)
{
int startindex = 0;
while(startindex < richTextBox1.TextLength)
{
int wordstartIndex = richTextBox1.Find(word, startindex, RichTextBoxFinds.None);
if (wordstartIndex != -1)
{
richTextBox1.SelectionStart = wordstartIndex;
richTextBox1.SelectionLength = word.Length;
richTextBox1.SelectionBackColor = Color.Yellow;
}
else
break;
startindex += wordstartIndex + word.Length;
}
}
}
For Clear Hightlight Use This Code
richTextBox1.SelectionStart = 0;
richTextBox1.SelectAll();
richTextBox1.SelectionBackColor = Color.White;
I have to find subtext in text without using builtin function of string.
public static void Main(string[] args)
{
string subtext = "polly";
string text = "polly put the katle on,polly put the katle on,polly put the katle on,we all have tea";
int i, j, found;
int strLen, wordLen;
strLen = text.Length;
wordLen = subtext.Length;
for (i = 0; i < strLen - wordLen; i++)
{
found = 1;
for (j = 0; j < wordLen; j++)
{
if (text[i + j] != subtext[j])
{
found = 0;
break;
}
}
if (found == 1)
{
Console.WriteLine(" found at index:", subtext, i);
Console.ReadLine();
}
}
}
I am not sure how long you would like to search, your current code seems to find all indexes (or at least that seems to be the intent)
Some things you could change however is instead of always starting the loop, you could validate the if the char at position i matches the first char of the subtext, and if not continue.
When you want to write the data to the console, don't forget to add the spaceholders for your arguments, like:
Console.WriteLine("found {0} at index: {1}", subtext, i);
For the rest, I guess your current implementation is okay, but you could add some validations, like ensuring that both texts are available, and if subtext is longer than the text, simply return -1 directly.
For a simple find of first index, I wrote this one up, it still looks pretty similar to yours
private static int FindIn( string text, string sub ) {
if (string.IsNullOrWhiteSpace( text ) || string.IsNullOrWhiteSpace( sub ) ) {
return string.IsNullOrWhiteSpace( sub ) ? 0 : -1;
}
if (text.Length < sub.Length) {
return -1;
}
for (int i = 0; i < text.Length - sub.Length; i++) {
if (text[i] != sub[0]) {
continue;
}
var matched = true;
for (int j = 1; j < sub.Length && i + j < text.Length; j++) {
if (text[i+j] != sub[j]) {
matched = false;
break;
}
}
if (matched) {
return i;
}
}
return -1;
}
Which you can play around with here
There are a lot of pattern-matching algorithms in this book, i will leave here c# implementation of Knuth-Morris-Pratt algorithm.
static int[] GetPrefix(string s)
{
int[] result = new int[s.Length];
result[0] = 0;
int index = 0;
for (int i = 1; i < s.Length; i++)
{
while (index >= 0 && s[index] != s[i]) { index--; }
index++;
result[i] = index;
}
return result;
}
static int FindSubstring(string pattern, string text)
{
int res = -1;
int[] pf = GetPrefix(pattern);
int index = 0;
for (int i = 0; i < text.Length; i++)
{
while (index > 0 && pattern[index] != text[i]) { index = pf[index - 1]; }
if (pattern[index] == text[i]) index++;
if (index == pattern.Length)
{
return res = i - index + 1;
}
}
return res;
}
If you are looking for all occurance of the subtect in the text you can use the following code:
public static void Main(string[] args)
{
string subtext = "polly";
string text = "polly put the katle on,polly put the katle on,polly put the katle on,we all have tea";
int index = 0;
int startPosition = 0;
bool found = false;
while (index < text.Length - 1)
{
if (subtext[0] == text[index])
{
startPosition = index;
index++;
for (int j = 1; j <= subtext.Length - 1; j++)
{
if (subtext[j] != text[index])
{
found = false;
break;
}
else
{
found = true;
}
index++;
}
}
if (found)
{
Console.WriteLine("{0} found at index: {1}", subtext, startPosition);
found = false;
}
index++;
}
Console.ReadLine();
}
If you are looking only for the first occurance add break in the "if (found)" condition
I need to create a winform to color expressions that starts with '#' that the user wrote in a textbox (can be a richTextBox or some Infragistics tool, does't really matter, it just have to work).
Exemple:
nothing #expression nothingelse
the #expression must be colored
I've tried to split the string from the textbox by the spaces to identify the word, but I wasn't abble to replace it correctly. Now I'm learning about Regex and trying to apply it to my problem.
I'm using C# in Visual Studio Community 2017.
EDIT
I changed a little the code from here, trying to fit it into my problem. But the limiter doesnt work as it is suppouse to. I belive that it is happening becouse that the selection starts in the '#', but there can be a space before that, so the code doesn't work.
Here is the code as I'm using it:
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
int current = richTextBox1.SelectionStart;
for (int i = 0; i < richTextBox1.Lines.Length; i++)
{
string line = richTextBox1.Lines[i];
int index = line.IndexOf(' '), lineFirstIndex = line.IndexOf('#');
if (index != -1 && lineFirstIndex != -1)
{
richTextBox1.Select(lineFirstIndex, index);
richTextBox1.SelectionColor = Color.Red;
}
else
{
lineFirstIndex = richTextBox1.GetFirstCharIndexFromLine(i);
richTextBox1.Select(lineFirstIndex, line.Length);
richTextBox1.SelectionColor = Color.Empty;
}
}
richTextBox1.Select(current, 0);
}
You need to use Regex like this :
foreach (string result in Regex.Split(TextBox1, #" #\w+"))
{
//do stuff...
}
I got the result that I wished, but there is a little problem. If i erase something with BackSpace, the backcolor starts to highlight everything.
I belive that this is something of the SelectionBackColor propriety, becouse it doesn't happend when I'm using SelectionColor.
The code:
private void FindAt() //acha arrobas
{
for(int r = 0; r <= tam; r++)
{
for(int z = 0; z < (tam - r); z++)
{
if (z > 8) break;
String temp = text.Substring(r, z);
char aux = '\0';
if (lista.FindStringExact(temp) != -1)
{
if (tam > r + z) aux = text.ElementAt(r + z);
if ((r > 0 && text.ElementAt(r - 1) == '#') && aux == ' ')
{
atIndex[0, ctrlAt] = r-1; //salva posição inicial da variavel, incluindo o # (por isso o -1)
atIndex[1, ctrlAt] = z + 1; //salva comprimento da variavel, + 1 por causa do # (segundo argumento da substring)
ctrlAt++;
}
}
}
}
}
and the function to color the substring:
private void ColorAt() //colore variaveis
{
for (int a = 0; a < ctrlAt; a++)
{
if (atIndex[0, a] != -1)
{
richTextBox1.Select(atIndex[0, a], atIndex[1, a]);
richTextBox1.SelectionBackColor = Color.Green;
}
else richTextBox1.SelectionBackColor = Color.Empty;
}
}
How can I add additional 30 character from the char that matches where in my code below.
private void CheckGarbageCharacters(string input)
{
var contentList = File.ReadAllLines(input).ToList();
int[] lineno = { 0 };
foreach (var line in contentList)
{
lineno[0]++;
foreach (var errorModel in from c in line
where c > '\u007F'
select new ErrorModel
{
Filename = Path.GetFileName(input),
LineNumber = lineno[0],
ErrorMessage = "Garbage character found.",
Text = c.ToString()
})
{
_errorList.Add(errorModel);
}
}
}
I'm not sure I fully understand your question but based on the code you have provided it seems like you are trying to do something like this...
~ Pseudo Code ~ This has not been tested ~
private void CheckGarbageCharacters(string filename)
{
var lines = File.ReadAllLines(filename).ToList();
for (int i = 0; i < lines.Count; i++)
{
var line = lines[i];
for (int j = 0; j < line.Length; j++)
{
var c = line[j];
if (c > '\u007F')
{
// Grab the next 30 characters after 'c'
var text = c.ToString();
for (int k = 0; k < 30; k++)
{
if ((j + k) > (line.Length - 1))
{
break;
}
text += line[j + k].ToString();
}
// Create the error message
var error = new ErrorModel()
{
Filename = Path.GetFileName(filename),
LineNumber = i,
ErrorMessage = "Garbage character found.",
Text = text
};
// Add the error to the list
_errorList.Add(error);
}
}
}
}
I'm not sure what you mean by "add additional 30 characters from the char that matches where in my code" though.
EDIT
I've updated my answer according to the information you've provided in the comments. I believe this is what you are trying to do here.
I need a C# function that takes 2 strings as an input and return an array of all possible combinations of strings.
private string[] FunctionName(string string1, string string2)
{
//code
}
The strings input will be in the following format:
string1: basement
string2: a*fa
Now what I need is all combinations of possible strings using the characters in String2 (ignoring the * symbols), and keeping them in the same character position like this:
baaement, baaefent, baaefena, basefent, basemena, etc.
EDIT:
This is not homework. I need this function for a piece of a program I am doing.
The following is the code I have so far but it has some bugs.
static List<string> combinations = new List<string>();
static void Main(string[] args)
{
//include trimming of input string
string FoundRes = "incoming";
string AltRes = "*2*45*78";
List<int> loc = new List<int>();
string word = "";
for (int i = 0; i < AltRes.Length; i++)
{
if (AltRes[i] != '*')
{
loc.Add(i);
word += AltRes[i];
}
}
generate(word);
string[] aaa = InsertSymbol(FoundRes, loc.ToArray(), AltRes, combinations);
Console.WriteLine("input string: " + FoundRes);
Console.WriteLine("Substitute string: " + AltRes);
Console.WriteLine("============Output============");
for (int j = 0; j < aaa.Length; j++)
{
Console.WriteLine(aaa[j]);
}
Console.ReadKey();
}//
private static void generate(string word)
{
// Add this word to combination results set
if (!combinations.Contains(word))
combinations.Add(word);
// If the word has only one character, break the recursion
if (word.Length == 1)
{
if (!combinations.Contains(word))
combinations.Add(word);
return;
}
// Go through every position of the word
for (int i = 0; i < word.Length; i++)
{
// Remove the character at the current position
// call this method with the String
generate(word.Substring(0, i) + word.Substring(i + 1));
}
}//
private static string[] InsertSymbol(string orig, int[] loc, string alternative, List<string> Chars)
{
List<string> CombinationsList = new List<string>();
string temp = "";
for (int i = 0; i < Chars.Count; i++)
{
temp = orig;
for (int j = 0; j < Chars[i].Length; j++)
{
string token = Chars[i];
if (alternative.IndexOf(token[j]) == loc[j])
{
temp = temp.Remove(loc[j], 1);
temp = temp.Insert(loc[j], token[j].ToString());
// int pos = sourceSubst.IndexOf(token[j]);
// sourceSubst = sourceSubst.Remove(pos, 1);
// sourceSubst = sourceSubst.Insert(pos, ".");
}
else
{
temp = temp.Remove(alternative.IndexOf(token[j]), 1);
temp = temp.Insert(alternative.IndexOf(token[j]), token[j].ToString());
}
}
CombinationsList.Add(temp);
}
return CombinationsList.ToArray();
}//
It does sound like homework. As a suggestion, I would ignore the first parameter and focus on getting all possible permutations of the second string. What's turned off, what's turned on, etc. From that list, you can easily come up with a method of swapping out characters of the first string.
On that note, I'm in the uncomfortable position of having a function ready to go but not wanting to post it because of the homework implication. I'd sure love for somebody to review it, though! And technically, there's two functions involved because I just happened to already have a generic function to generate subsets lying around.
Edit: OP says it isn't homework, so here is what I came up with. It has been refactored a bit since the claim of two functions, and I'm more than open to criticism.
using System;
using System.Collections.Generic;
using System.Text;
class Program
{
static void Main()
{
string original = "phenomenal";
string pattern = "*xo**q*t**";
string[] replacements = StringUtility.GetReplacementStrings(original, pattern, true);
foreach (string replacement in replacements)
Console.WriteLine(replacement);
Console.Read();
}
public static class StringUtility
{
public static string[] GetReplacementStrings(string original, string pattern, bool includeOriginal)
{
// pattern and original might not be same length
int maxIndex = Math.Max(original.Length, pattern.Length);
List<int> positions = GetPatternPositions(pattern, maxIndex, '*');
List<int[]> subsets = ArrayUtility.CreateSubsets(positions.ToArray());
List<string> replacements = GenerateReplacements(original, pattern, subsets);
if (includeOriginal)
replacements.Insert(0, original);
return replacements.ToArray();
}
private static List<string> GenerateReplacements(string original, string pattern, List<int[]> subsets)
{
List<string> replacements = new List<string>();
char[] temp = new char[original.Length];
foreach (int[] subset in subsets)
{
original.CopyTo(0, temp, 0, original.Length);
foreach (int index in subset)
{
temp[index] = pattern[index];
}
replacements.Add(new string(temp));
}
return replacements;
}
private static List<int> GetPatternPositions(string pattern, int maxIndex, char excludeCharacter)
{
List<int> positions = new List<int>();
for (int i = 0; i < maxIndex; i++)
{
if (pattern[i] != excludeCharacter)
positions.Add(i);
}
return positions;
}
}
public static class ArrayUtility
{
public static List<T[]> CreateSubsets<T>(T[] originalArray)
{
List<T[]> subsets = new List<T[]>();
for (int i = 0; i < originalArray.Length; i++)
{
int subsetCount = subsets.Count;
subsets.Add(new T[] { originalArray[i] });
for (int j = 0; j < subsetCount; j++)
{
T[] newSubset = new T[subsets[j].Length + 1];
subsets[j].CopyTo(newSubset, 0);
newSubset[newSubset.Length - 1] = originalArray[i];
subsets.Add(newSubset);
}
}
return subsets;
}
}
}
since it's hopw work I'd only suggest some way to solve the problem rather than writing the code.
if you loop the second parameter every time you hit a letter you'll have to options either use the letter from the first argument or the letter from the second. collect all these optins together with the index. keep a list of the parts from the first argument that will never change. iterate thorugh those two lists to created all the possible permutations
Decimal to Binary converted code is stolon copied from here.
static void Main()
{
string string1 = "basement";
string string2 = "**a*f**a";
string[] result = GetCombinations(string1, string2);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
private static string[] GetCombinations(string string1, string string2)
{
var list = new List<List<char>> { new List<char>(), new List<char>() };
var cl = new List<char>();
List<string> result = new List<string>();
for (int i = 0; i < string1.Length; i++)
{
if (string2[i] == '*')
{
cl.Add(string1[i]);
}
else
{
list[0].Add(string1[i]);
list[1].Add(string2[i]);
}
}
int l = list[0].Count;
for (int i = 0; i < (Int64)Math.Pow(2.0,l); i++)
{
string s = ToBinary(i, l);
string ss = "";
int x = 0;
int y = 0;
for (int I = 0; I < string1.Length; I++)
{
if (string2[I] == '*')
{
ss += cl[x].ToString();
x++;
}
else
{
ss += (list[int.Parse(s[y].ToString())][y]);
y++;
}
}
result.Add(ss);
}
return result.ToArray<string>();
}
public static string ToBinary(Int64 Decimal, int width)
{
Int64 BinaryHolder;
char[] BinaryArray;
string BinaryResult = "";
while (Decimal > 0)
{
BinaryHolder = Decimal % 2;
BinaryResult += BinaryHolder;
Decimal = Decimal / 2;
}
BinaryArray = BinaryResult.ToCharArray();
Array.Reverse(BinaryArray);
BinaryResult = new string(BinaryArray);
var d = width - BinaryResult.Length;
if (d != 0) for (int i = 0; i < d; i++) BinaryResult = "0" + BinaryResult;
return BinaryResult;
}
which password cracker do you want to program? :)
how about
if string2 contains '*'
foreach(char ch in string1)
replace first * with ch,
execute FunctionName
else
print string2