我正在尝试制作一个搜索代码,允许您在两个方向(右侧和左侧)查找文本。我已经制作了一个代码,可以向右搜索文本,但不是向左搜索。
我正在尝试做的例子:
example1 example2 example3 example4
让我们说我发现'example3'(向右),现在我需要找到example2(向左)我将如何制作该代码?
问题:如何制作一个搜索文本向左(反向)的代码?
以下是我尝试搜索左侧文本的示例:
try
{
richTextBox1.Focus();
richTextBox1.Find(findwhat, findPos, RichTextBoxFinds.Reverse);
richTextBox1.Select(findPos, findwhat.Length);
findPos += findwhat.Length;
}
catch
{
findPos = 0;
}
一种简单的方法是向后迭代并检查当前字符串是否包含给定的字符串:
public static class Extension{
public static bool ReverseContains(this string container, string token)
{
if (string.IsNullOrEmpty (container) || string.IsNullOrEmpty (token))
{
return false;
}
int offset = container.Length - token.Length;
string start = container.Substring (offset);
for (int i = offset - 1; i >= 0; i--)
{
start = container[i] + start;
if(start.Contains(token)){ return true;}
}
return false;
}
}
如果容器包含令牌,则返回true。搜索将从最后开始。
它没有考虑大写/小写,因此在“一堆单词”中找不到“单词”。如果需要,您必须添加一个额外的参数以使其全部小写。
string str = "My string with many words to be found";
string wordToFind = "words";
bool result = str.ReverseContains(wordToFind);
我有同样的需要。这是一个可用的WinForms应用程序来演示。解决这个问题的灵感来自于CodeGuru上的线程"RichTextBox reverse find"。
在我的演示中,RTB预装了文本并且是只读的,即我没有尝试解决与使用可编辑RTB实现查找前进和后退相关的问题。
表单有以下控件:
ClsProgram.cs
using System;
using System.Windows.Forms;
namespace RichTextBoxFindWithReverse
{
static class ClsProgram
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ClsFormMain());
}
}
/// <summary>
/// https://stackoverflow.com/questions/1550293/stopping-textbox-flicker-during-update
/// </summary>
public static class ControlExtensions
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool LockWindowUpdate(IntPtr hWndLock);
public static void Suspend(this Control control)
{
LockWindowUpdate(control.Handle);
}
public static void Resume(this Control control)
{
LockWindowUpdate(IntPtr.Zero);
}
}
}
ClsFormMain.cs
using System;
using System.Windows.Forms;
namespace RichTextBoxFindWithReverse
{
public partial class ClsFormMain : Form
{
public ClsFormMain()
{
InitializeComponent();
objRichTextBox.SelectionDataIsInteresting += ObjRichTextBox_SelectionDataIsInteresting;
}
private void Form1_Load(object sender, EventArgs e)
{
objRichTextBox.Text = "cat\ndog\nsnail\nOtter\ntigerelephant\ncatcatcat\ncatcatdog\ncatcatsnail\ncatcatOtter\ncatcattiger\ncatcatelephant\ncatdogcat\ncatdogdog\ncatdogsnail\ncatdogOtter\ncatdogtiger\ncatdogelephant\ncatsnailcat\ncatsnaildog\ncatsnailsnail\ncatsnailOtter\ncatsnailtiger\ncatsnailelephant\ncatOttercat\ncatOtterdog\ncatOttersnail\ncatOtterOtter\ncatOttertiger\ncatOtterelephant\ncattigercat\ncattigerdog\ncattigersnail";
objButtonFind.Enabled = false;
objReverse.Enabled = false;
objCheckBoxMatchCase.Enabled = false;
objCheckBoxWholeWord.Enabled = false;
}
private void ObjTextBoxSearchWord_TextChanged(object sender, EventArgs e)
{
if (objRichTextBox.Text.Length > 0 && objTextBoxSearchWord.Text.Length > 0)
{
objButtonFind.Enabled = true;
objReverse.Enabled = true;
objCheckBoxMatchCase.Enabled = true;
objCheckBoxWholeWord.Enabled = true;
}
else
{
objButtonFind.Enabled = false;
objReverse.Enabled = false;
objCheckBoxMatchCase.Enabled = false;
objCheckBoxWholeWord.Enabled = false;
}
}
private void ObjButtonFind_Click(object sender, EventArgs e)
{
string options = "";
if (!objCheckBoxMatchCase.Checked && !objCheckBoxWholeWord.Checked)
{
options = "Don't match case.\nMatch on partial word or whole word.";
}
else if (!objCheckBoxMatchCase.Checked && objCheckBoxWholeWord.Checked)
{
options = "Don't match case.\nMatch on whole word only.";
}
else if (objCheckBoxMatchCase.Checked && !objCheckBoxWholeWord.Checked)
{
options = "Match case.\nMatch on partial word or whole word.";
}
else //(objCheckBoxMatchCase.Checked && objCheckBoxWholeWord.Checked)
{
options = "Match case.\nMatch on whole word only.";
}
bool found = objRichTextBox.FindTextCustom(objTextBoxSearchWord.Text, objReverse.Checked, objCheckBoxMatchCase.Checked, objCheckBoxWholeWord.Checked);
if (!found)
{
System.Windows.Forms.MessageBox.Show(string.Format("Can't find '{0}'.\n\nYour options:\n\n{1}", objTextBoxSearchWord.Text, options), "RichTextBox Find With Reverse", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
/// <summary>
/// Display rich text box selection data
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ObjRichTextBox_SelectionDataIsInteresting(object sender, ClsRichTextBoxSelectionArgs e)
{
objTextBoxStartPos.Text = e.SelectionStart.ToString();
}
}
}
ClsRichTextBox.cs
using System;
using System.Drawing;
using System.Windows.Forms;
namespace RichTextBoxFindWithReverse
{
class ClsRichTextBox : RichTextBox
{
ClsFindMetadata objFindMetadata = null;
ClsRichTextBoxSelectionArgs objRichTextBoxSelectionArgs = null;
public ClsRichTextBox() : base()
{
SelectionChanged += ClsRichTextBox_SelectionChanged;
objFindMetadata = new ClsFindMetadata();
objRichTextBoxSelectionArgs = new ClsRichTextBoxSelectionArgs();
}
/// <summary>
/// Clear the find data and highlighting (yellow background) if the user clicks on the text in the control
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClsRichTextBox_SelectionChanged(object sender, EventArgs e)
{
ClearLastFind();
objRichTextBoxSelectionArgs.Set(SelectionStart);
OnSelectionDataIsInteresting(objRichTextBoxSelectionArgs);
}
/// <summary>
/// If last find data is available (findIndex, findLength), clear the highlighting
/// </summary>
public void ClearLastFind()
{
SelectionChanged -= ClsRichTextBox_SelectionChanged;
ControlExtensions.Suspend(this);
int saveSelectionStart = SelectionStart;
if (objFindMetadata.findStart != -1)
{
objFindMetadata.ClearFind();
}
if (objFindMetadata.highLightStart != -1)
{
Select(objFindMetadata.highLightStart, objFindMetadata.highLightLength);
objFindMetadata.ClearHighLight();
SelectionBackColor = Color.White;
SelectionLength = 0;
}
SelectionStart = saveSelectionStart;
ControlExtensions.Resume(this);
SelectionChanged += ClsRichTextBox_SelectionChanged;
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
/// <summary>
/// If searchText is found, returns true. Otherwise, returns false
/// </summary>
/// <param name="searchText"></param>
/// <param name="isReverse"></param>
/// <param name="isMatchCase"></param>
/// <param name="isWholeWord"></param>
/// <returns></returns>
public bool FindTextCustom(string searchText, bool isReverse, bool isMatchCase, bool isWholeWord)
{
int previousSaveFindIndex = objFindMetadata.findStart;
int previousSaveFindLength = objFindMetadata.findLength;
int localForwardOffset = 1;
int saveSelectionStart = SelectionStart;
int saveSelectionLength = SelectionLength;
int indexToplineCharOne = GetCharIndexFromPosition(new Point(0, 0));
bool found = false;
SelectionChanged -= ClsRichTextBox_SelectionChanged;
ControlExtensions.Suspend(this);
SelectionStart = saveSelectionStart;
if (saveSelectionStart == 0 && objFindMetadata.findStart == -1)
{
localForwardOffset = 0;
}
if (!isReverse && !isMatchCase && !isWholeWord)
{
objFindMetadata.findStart = Find(searchText, Math.Min(SelectionStart + localForwardOffset, TextLength), Text.Length, RichTextBoxFinds.None);
}
else if (!isReverse && !isMatchCase && isWholeWord)
{
objFindMetadata.findStart = Find(searchText, Math.Min(SelectionStart + localForwardOffset, TextLength), Text.Length, RichTextBoxFinds.WholeWord);
}
else if (!isReverse && isMatchCase && !isWholeWord)
{
objFindMetadata.findStart = Find(searchText, Math.Min(SelectionStart + localForwardOffset, TextLength), Text.Length, RichTextBoxFinds.MatchCase);
}
else if (!isReverse && isMatchCase && isWholeWord)
{
objFindMetadata.findStart = Find(searchText, Math.Min(SelectionStart + localForwardOffset, TextLength), Text.Length, RichTextBoxFinds.MatchCase | RichTextBoxFinds.WholeWord);
}
else if (isReverse && !isMatchCase && !isWholeWord)
{
objFindMetadata.findStart = Find(searchText, 0, SelectionStart, RichTextBoxFinds.Reverse);
}
else if (isReverse && !isMatchCase && isWholeWord)
{
objFindMetadata.findStart = Find(searchText, 0, SelectionStart, RichTextBoxFinds.WholeWord | RichTextBoxFinds.Reverse);
}
else if (isReverse && isMatchCase && !isWholeWord)
{
objFindMetadata.findStart = Find(searchText, 0, SelectionStart, RichTextBoxFinds.MatchCase | RichTextBoxFinds.Reverse);
}
else // (isReverse && isMatchCase && isWholeWord)
{
objFindMetadata.findStart = Find(searchText, 0, SelectionStart, RichTextBoxFinds.MatchCase | RichTextBoxFinds.WholeWord | RichTextBoxFinds.Reverse);
}
found = false;
if (objFindMetadata.findStart >= 0)
{
if (!isReverse)
{
if (saveSelectionStart <= objFindMetadata.findStart)
{
found = true;
}
}
else
{
if (SelectionStart < saveSelectionStart)
{
found = true;
}
}
}
if (found)
{
// ClearLastFind isn't applicable because it clears find metadata. Just clear the highlight
if (previousSaveFindIndex != -1)
{
Select(objFindMetadata.highLightStart, objFindMetadata.highLightLength);
objFindMetadata.ClearHighLight();
SelectionBackColor = Color.White;
}
objFindMetadata.highLightStart = objFindMetadata.findStart;
objFindMetadata.highLightLength = objFindMetadata.findLength = searchText.Length;
Select(objFindMetadata.findStart, objFindMetadata.findLength);
SelectionBackColor = Color.Yellow;
SelectionLength = 0;
}
else
{
objFindMetadata.ClearFind();
SelectionLength = 0;
SelectionStart = saveSelectionStart;
}
ControlExtensions.Resume(this);
objRichTextBoxSelectionArgs.Set(SelectionStart);
OnSelectionDataIsInteresting(objRichTextBoxSelectionArgs);
SelectionChanged += ClsRichTextBox_SelectionChanged;
Focus();
return found;
}
/// <summary>
/// Method used to invoke the event that is used to report RTB SelectionStart to interested parties
/// https://docs.microsoft.com/en-us/dotnet/api/system.eventhandler-1?view=netframework-4.7.2
/// </summary>
/// <param name="e"></param>
protected virtual void OnSelectionDataIsInteresting(ClsRichTextBoxSelectionArgs e)
{
SelectionDataIsInteresting?.Invoke(this, e);
}
/// <summary>
/// Event used to report RTB SelectionStart to interested parties
/// </summary>
public event EventHandler<ClsRichTextBoxSelectionArgs> SelectionDataIsInteresting;
/// <summary>
/// Class used to record state of find results and find highlighting
/// </summary>
private class ClsFindMetadata
{
internal int findStart = -1;
internal int findLength = -1;
internal int highLightStart = -1;
internal int highLightLength = -1;
internal void ClearFind()
{
findStart = -1;
findLength = -1;
}
internal void ClearHighLight()
{
highLightStart = -1;
highLightLength = -1;
}
}
}
/// <summary>
/// Class used to report RTB SelectionStart to interested parties
/// </summary>
public class ClsRichTextBoxSelectionArgs : EventArgs
{
internal void Set(int selectionStart)
{
SelectionStart = selectionStart;
}
public int SelectionStart { get; set; }
}
}
public string searchExpress = string.Empty;
public int findPos = 0;
private void reverseSearchButton_Click(object sender, EventArgs e)
{
// this is to check whether new search term is written in searchbox toolStripTextBox2
string findterm = string.Empty;
findterm = toolStripTextBox2.Text;
if (findterm != searchExpress)
{
findPos = GetRichTextBox().TextLength;
searchExpress = findterm;
}
if (toolStripTextBox2.Text.Length > 0)
{
try
{
findPos = GetRichTextBox().Find(findterm, 0, GetRichTextBox().SelectionStart , RichTextBoxFinds.Reverse);
GetRichTextBox().Select(findPos, toolStripTextBox2.Text.Length);
GetRichTextBox().ScrollToCaret();
GetRichTextBox().Focus();
findPos += toolStripTextBox2.Text.Length + 1;
}
catch
{
findPos = 0;
}
}
}