How do i highlight a background text in a text file? - c#

I have a dictionary collection that stores the starting position and the charecter values of a text file.
For example: a sample text file (a.txt) may contain text like "how are you? how do you do?"
I have indexed the above text as follows
Dictionary<long,string> charLocation = new Dictionary<long,string>();
charLocation[0] = "how"
charLocation[1] = "ow"
charLocation[2] = "w"
charLocation[4] = "are"
charLocation[6] = "e"
charLocation[5] = "re"
charLocation[11] = "?"
charLocation[9] = "ou?"
charLocation[10] = "u?"
charLocation[8] = "you?"
charLocation[13] = "how"
charLocation[14] = "ow"
charLocation[15] = "w"
charLocation[17] = "do"
charLocation[18] = "o"
charLocation[21] = "ou"
charLocation[22] = "u"
charLocation[20] = "you"
charLocation[26] = "?"
charLocation[24] = "do?"
charLocation[25] = "o?"
Now, I want to highlight each occurrence of "how" or "do" in the text file.
For this I want to first do a lookup in the dictionary collection and find each occurrence of the string, then open the text file and highlight the text for each occurrence.
How can i do this?

Not tested, but this should works.
public string HighLight (int startPoint, string text, string word)
{
if (startPoint > = 0)
{
int startIndex = text.indexOf (word, startPoint);
if (startIndex >= 0)
{
StringBuilder builder = new StringBuilder ();
builder.Append (text.Substring ( 0, startIndex));
builder.Append ("<strong>");
builder.Append (text.Substring (startIndex + 1, word.Length));
builder.Append ("</strong>");
builder.Append (text.Substring (startIndex + word.Length + 1));
return HighLight ((startIndex + "<strong>".Length + "</strong>".Length + word.Length, builder.ToString (), word);
}
}
//Word not found.
return text;
}
So you could do :
string myText = "how are you? how do you do?";
string hightLightedText = HighLight (0, myText, "how");
And if my code has no errors, that would returns "<strong>how</strong> are you? <strong>how</strong> do you do?";
Then you can repalce <strong> and </strong> with wathever you want to "highlight" your text.

Related

Can't properly rebuild a string with Replacement values from Dictionary

I am trying to build a file using a template. I am processing the file in a while loop line by line. The first section of the file, first 35 lines are header information. The infromation is surrounded by # signs. Take this string for example:
Field InspectionStationID 3 {"PVA TePla #WSM#", "sw#data.tool_context.TOOL_SOFTWARE_VERSION#", "#data.context.TOOL_ENTITY#"}
The expected output should be:
Field InspectionStationID 3 {"PVA TePla", "sw0.2.002", "WSM102"}
This header section uses a different mapping than the rest of the file so I wanted to parse the file line by line from top to bottom and use a different logic for each section so that I don't waste time parsing the entire file at once multiple times for different sections.
The logic uses two dictionaries populated from an xml file. Because the file has mutliple tables, I combined them in the two dictionaries like so:
var headerCdataIndexKeyVals = Dictionary<string, int>(){
{"data.tool_context.TOOL_SOFTWARE_VERSION", 1},
{"data.context.TOOL_ENTITY",0}
};
var headerCdataArrayKeyVals = new Dictionary<string, List<string>>();
var tool_contextCdataList = new list <string>{"HM654", "sw0.2.002"};
var contextCdataList = new List<string>{"WSM102"}
headerCdataArrayKeyVals.add("tool_context", tool_contextCdataList);
headerCdataArrayKeyVals.add("context", contextCdataList);
To help me map the values to their respective positions in the string in one go and without having to loop through multiple dictionaries.
I am using the following logic:
public static string FindSubsInDelimetersAndReturn(string str, char openDelimiter, char closeDelimiter, HeaderMapperData mapperData )
{
string newString = string.Empty;
// Stores the indices of
Stack <int> dels = new Stack <int>();
for (int i = 0; i < str.Length; i++)
{
var let = str[i];
// If opening delimeter
// is encountered
if (str[i] == openDelimiter && dels.Count == 0)
{
dels.Push(i);
}
// If closing delimeter
// is encountered
else if (str[i] == closeDelimiter && dels.Count > 0)
{
// Extract the position
// of opening delimeter
int pos = dels.Peek();
dels.Pop();
// Length of substring
int len = i - 1 - pos;
// Extract the substring
string headerSubstring = str.Substring(pos + 1, len);
bool hasKey = mapperData.HeaderCdataIndexKeyVals.TryGetValue(headerSubstring.ToUpper(), out int headerCdataIndex);
string[] headerSubstringSplit = headerSubstring.Split('.');
string headerCDataVal = string.Empty;
if (hasKey)
{
if (headerSubstring.Contains("CONTAINER.CONTEXT", StringComparison.OrdinalIgnoreCase))
{
headerCDataVal = mapperData.HeaderCdataArrayKeyVals[headerSubstringSplit[1].ToUpper() + '.' + headerSubstringSplit[2].ToUpper()][headerCdataIndex];
//mapperData.HeaderCdataArrayKeyVals[]
}
else
{
headerCDataVal = mapperData.HeaderCdataArrayKeyVals[headerSubstringSplit[1].ToUpper()][headerCdataIndex];
}
string strToReplace = openDelimiter + headerSubstring + closeDelimiter;
string sub = str.Remove(i + 1);
sub = sub.Replace(strToReplace, headerCDataVal);
newString += sub;
}
else if (headerSubstring == "WSM" && closeDelimiter == '#')
{
string sub = str.Remove(len + 1);
newString += sub.Replace(openDelimiter + headerSubstring + closeDelimiter, "");
}
else
{
newString += let;
}
}
}
return newString;
}
}
But my output turns out to be:
"\tFie\tField InspectionStationID 3 {\"PVA TePla#WSM#\", \"sw0.2.002\tField InspectionStationID 3 {\"PVA TePla#WSM#\", \"sw#data.tool_context.TOOL_SOFTWARE_VERSION#\", \"WSM102"
Can someone help understand why this is happening and how I can go about correcting it so I get the output:
Field InspectionStationID 3 {"PVA TePla", "sw0.2.002", "WSM102"}
Am i even trying to solve this the right way or is there a better cleaner way to do it? Btw if the key is not in the dictionary I replace it with empty string

How to combine two texts?

I have a Single line text box and Multiline text box, and want to include a word into the Single line text box with the words in Multiline text box per line
Like this :
Single line text: "Hello"(I have to use variables)<br>
Multiline words:
<br>
1998<br>
1999<br>
2000
Expected results:
Hello1998
Hello1999
Hello2000
Pls Help me
I use the below code, but it is not working just with the Single line text box and I have to manipulate by both text boxes:
string left = string.Format(add.Text , Environment.NewLine);
string right = string.Format(textBox1.Text, Environment.NewLine);
string[] leftSplit = left.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
string[] rightSplit = right.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
string output = "";
if (leftSplit.Length == rightSplit.Length)
{
for (int i = 0; i < leftSplit.Length; i++)
{
output += leftSplit[i] + ":" + rightSplit[i] + Environment.NewLine;
}
}
result.Text = output;
Could you please advise me on the right approach?
If you have single line only one word, then no need split it into an array.
Lets consider it as a string left = "Hello";
and textbox1 contains multiline words i.e.
string right = string.Format(textBox1.Text, Environment.NewLine); // right variable contains 1998 \n 1999 \n 2000
Then you can try below approach
var concatString = right.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Select(x => left + x);
string result = string.Join(Environment.NewLine , concatString);
.Net Fiddle
Output :
Hello1998
Hello1999
Hello2000
TextBox.GetLineText(int) will help you:
var singlelineText = singlelineTextBox.Text;
var composedLines = new List<string>();
for (var i = 0; i < multilineineTextBox.LineCount; i++)
{
composedLines.Add(singlelineText + multilineineTextBox.GetLineText(i));
}
result.Text = string.Join(EnvironmentNewline, composedLines);

It's possible to replaces chars when Shift key is pressed?

Hello Stack Overflow community!
This is my function that catches every pressed key:
public static string GetBuffKeys()
{
string buffer = "";
foreach (System.Int32 i in Enum.GetValues(typeof(Keys)))
{
if (GetAsyncKeyState(i) == -32767)
buffer += Enum.GetName(typeof(Keys), i);
}
return buffer;
}
To have a little formating, I using this function that replaces me characters with new one:
public static class KeyControl
{
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(int vKey);
public static string ReplaceChars(string text)
{
text = text.Replace("Space", " ");
text = text.Replace("Delete", "<Del>");
text = text.Replace("LShiftKey", "");
text = text.Replace("ShiftKey", "");
text = text.Replace("OemQuotes", "!");
text = text.Replace("Oemcomma", "?");
text = text.Replace("D8", "á");
text = text.Replace("D2", "ě");
text = text.Replace("D3", "š");
text = text.Replace("D4", "č");
text = text.Replace("D5", "ř");
text = text.Replace("D6", "ž");
text = text.Replace("D7", "ý");
text = text.Replace("D9", "í");
text = text.Replace("D0", "é");
text = text.Replace("D1", "+");
text = text.Replace("Back", "<==");
text = text.Replace("LButton", "");
text = text.Replace("RButton", "");
text = text.Replace("NumPad", "");
text = text.Replace("OemPeriod", ".");
text = text.Replace("OemSemicolon", ",");
text = text.Replace("Oem4", "/");
text = text.Replace("LControlKey", "");
text = text.Replace("ControlKey", "");
text = text.Replace("Enter", "<ENT>");
text = text.Replace("Shift", "");
text = text.Replace("CapsLock", "");
text = text.Replace("Oem6", "(");
return text;
}
But I want replace D* (D1, for example) with number, if Shift key is pressed. It is possible? And if not, what is better method for key logging than buffer all pressed keys? Thanks a lot!
You can use Control.ModifierKeys (WinForms) or Keyboard.Modifiers (WPF) to see what modifier keys are currently pressed. For instance:
var modifiers = Control.ModifierKeys;
if (modifiers != Keys.None)
{
text = text = "[" + modifiers.ToString() + "]";
}
Note that Keyboard.Modifiers are a bitmask field. Thus the following code clears a specific modifier when they current keypress event corresponds to that modifier:
public static string GetBuffKeys()
{
var allModifierKeys = new Dictionary<Keys, Keys> { { Keys.ControlKey, Keys.Control }, {Keys.ShiftKey, Keys.Shift}, {Keys.Menu, Keys.Alt }};
var modifiers = Control.ModifierKeys;
string buffer = "";
foreach (var key in Enum.GetValues(typeof(Keys)).OfType<Keys>().Distinct()) // The enum seems to have duplicated values for Enter and Return. Uniquify them.
{
if (GetAsyncKeyState((int)key) == -32767)
{
buffer += ReplaceChars(Enum.GetName(typeof(Keys), key));
if (allModifierKeys.Keys.Contains(key))
modifiers &= ~(allModifierKeys[key]); // Remove this modifier to prevent double printing.
}
}
if (modifiers != Keys.None)
{
buffer = buffer + " [" + modifiers.ToString() + "]";
}
return buffer;
}

Sorting lines in winforms richtextbox preserving RTF formatting

Is there any way to sort lines in winforms richtextbox preserving RTF formatting?
var lines = edit.Lines.OrderBy(s => s);
edit.Lines = lines.ToArray();
do the job fine, but, obviously, loosing any RTF formatting.
I have slightly changed the snippet of TaW:
1. Adding "unique" might break the very first line formatting
2. Besides "\par" tag there is also "\pard"
Here is a snippet (thanks again to TaW!):
private void cmdSort_Click(object sender, EventArgs e)
{
const string PARD = "\\pard";
var pard = Guid.NewGuid().ToString();
var pos1 = edit.Rtf.IndexOf(PARD, StringComparison.Ordinal) + PARD.Length;
if (pos1 < 0) return;
var header = edit.Rtf.Substring(0, pos1);
var body = edit.Rtf.Substring(pos1);
body = body.Replace("\\pard", pard);
var lines = body.Split(new[] { "\\par" }, StringSplitOptions.None);
var lastFormat = "";
var sb = new StringBuilder();
var rtfLines = new SortedList<string, string>();
foreach (var line in lines)
{
var ln = line.Replace(pard, "\\pard");
var temp = ln.Replace("\r\n", "").Trim();
if (temp.Length > 0 && temp[0] != '\\')
{
rtfLines.Add(temp.Trim(), lastFormat + " " + ln);
}
else
{
var pos2 = temp.IndexOf(' ');
if (pos2 < 0)
{
rtfLines.Add(temp.Trim(), ln);
}
else
{
rtfLines.Add(temp.Substring(pos2).Trim(), ln);
lastFormat = temp.Substring(0, pos2);
}
}
}
foreach (var key in rtfLines.Keys.Where(key => key != "}"))
{
sb.Append(rtfLines[key] + "\\par");
}
edit.Rtf = header + sb;
}
Here is a code snippet that seems to work if the file has neither images nor tables embedded..
It uses two RTF boxes. In my tests they sorted alright and kept all formatting intact.
private void button4_Click(object sender, EventArgs e)
{
string unique = Guid.NewGuid().ToString() ;
richTextBox1.SelectionStart = 0;
richTextBox1.SelectionLength = 0;
richTextBox1.SelectedText = unique;
int pos1 = richTextBox1.Rtf.IndexOf(unique);
if (pos1 >= 0)
{
string header = richTextBox1.Rtf.Substring(0, pos1);
string header1 = "";
string header2 = "";
int pos0 = header.LastIndexOf('}') + 1;
if (pos0 > 1) { header1 = header.Substring(0, pos0); header2 = header.Substring(pos0); }
// after the header comes a string of formats to start the document
string[] formats = header2.Split('\\');
string firstFormat = "";
string lastFormat = "";
// we extract a few important character formats (bold, italic, underline, font, color)
// to keep with the first line which will be sorted away
// the lastFormat variable holds the last formatting encountered
// so we can add it to all lines without formatting
// (and of course we are really talking about paragraphs)
foreach (string fmt in formats)
if (fmt[0]=='b' || ("cfiu".IndexOf(fmt[0]) >= 0 && fmt.Substring(0,2)!="uc") )
lastFormat += "\\" + fmt; else firstFormat += "\\" + fmt;
// add the rest to the header
header = header1 + firstFormat;
// now we remove our marker from the body and split it into paragraphs
string body = richTextBox1.Rtf.Substring(pos1);
string[] lines = body.Replace(unique, "").Split(new string[] { "\\par" }, StringSplitOptions.None);
StringBuilder sb = new StringBuilder();
// the soteredlist will contain the unformatted text as key and the formatted one as valaue
SortedList<string, string> rtfLines = new SortedList<string, string>();
foreach (string line in lines)
{
// cleanup
string line_ = line.Replace("\r\n", "").Trim();
if (line_[0] != '\\' ) rtfLines.Add(line_, lastFormat + " " + line);
else
{
int pos2 = line_.IndexOf(' ');
if (pos2 < 0) rtfLines.Add(line_, line);
else
{
rtfLines.Add(line_.Substring(pos2).Trim(), line);
lastFormat = line_.Substring(0, pos2);
}
}
}
foreach (string key in rtfLines.Keys) if (key != "}") sb.Append(rtfLines[key] + "\\par");
richTextBox2.Rtf = header + sb.ToString();
}
Of course this is really q&d and not ready for serious production; but it looks like a start.
EDIT 2: I changed the code to fix a bug with the first line's format and added some comments. This should work a lot better, but is still a hack that must be adapted to the real input files..
RichTextBox has a property Rtf that would keep RTF formatting.
[BrowsableAttribute(false)]
public string Rtf { get; set; }

Is there a better way to implement Shift+Tab or Decrease Indent?

this is how i implemented Shift-Tab or decrease indent... the result on screenr
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift && e.Key == Key.Tab)
{
// Shift+Tab
int selStart = txtEditor.SelectionStart;
int selLength = txtEditor.SelectionLength;
string selText = txtEditor.SelectedText;
string text = txtEditor.Text;
// find new lines that are followed by 1 or more spaces
Regex regex = new Regex(Environment.NewLine + #"(\s+)");
Match m = regex.Match(selText);
string spaces;
while (m.Success)
{
GroupCollection grps = m.Groups;
spaces = grps[1].Value;
int i = 0;
// remove 1 space on each loop to a max of 4 spaces
while (i < 4 && spaces.Length > 0)
{
spaces = spaces.Remove(0, 1);
i++;
}
// update spaces in selText
selText = selText.Remove(grps[1].Index, grps[1].Length).Insert(grps[1].Index, spaces);
m = regex.Match(selText, grps[1].Index + spaces.Length);
}
// commit changes to selText to text
text = text.Remove(selStart, selLength).Insert(selStart, selText);
// decrease indent of 1st line
// - find 1st character of selection
regex = new Regex(#"\w");
m = regex.Match(text, selStart);
int start = selStart;
if (m.Success) {
start = m.Index;
}
// - start search for spaces
regex = new Regex(Environment.NewLine + #"(\s+)", RegexOptions.RightToLeft);
m = regex.Match(text, start);
if (m.Success) {
spaces = m.Groups[1].Value;
int i = 0;
while (i < 4 && spaces.Length > 0) {
spaces = spaces.Remove(0, 1); // remove 1 space
i++;
}
text = text.Remove(m.Groups[1].Index, m.Groups[1].Length).Insert(m.Groups[1].Index, spaces);
selStart = m.Groups[1].Index;
}
txtEditor.Text = text;
txtEditor.SelectionStart = selStart;
txtEditor.SelectionLength = selText.Length;
e.Handled = true;
}
the code looks messy and i wonder if theres a better way.
Personally, I wouldn't use Regex for this.
Untested, probably needs modification:
public static class StringExtensions
{
// Removes leading white-spaces in a string up to a maximum
// of 'level' characters
public static string ReduceIndent(this string line, int level)
{
// Produces an IEnumerable<char> with the characters
// of the string verbatim, other than leading white-spaces
var unindentedChars = line.SkipWhile((c, index) => char.IsWhiteSpace(c) && index < level);
return new string(unindentedChars.ToArray());
}
// Applies a transformation to each line of a string and returns the
// transformed string
public static string LineTransform(this string text, Func<string,string> transform)
{
//Splits the string into an array of lines
var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
//Applies the transformation to each line
var transformedLines = lines.Select(transform);
//Joins the transformed lines into a new string
return string.Join(Environment.NewLine, transformedLines.ToArray());
}
}
...
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift && e.Key == Key.Tab)
{
// Reduces the indent level of the selected text by applying the
// 'ReduceIndent' transformation to each line of the text.
string replacement = txtEditor.SelectedText
.LineTransform(line => line.ReduceIndent(4));
int selStart = txtEditor.SelectionStart;
int selLength = txtEditor.SelectionLength;
txtEditor.Text = txtEditor.Text
.Remove(selStart, selLength)
.Insert(selStart, replacement);
txtEditor.SelectionStart = selStart;
txtEditor.SelectionLength = replacement.Length;
e.Handled = true;
}
EDIT:
Added comments to the code as per the request of the OP.
For more info:
Extension Methods
Func<T, TResult> delegate
Enumerable.SkipWhile extension method
Lambda Expressions
I'm thinking freely as I have never implemented a text editor.
What if you represent each line by an object with an indentation property, which is reflected in the rendering of the line. Then it would be easy to increase and decrease the indent.

Categories