Xwork's Blog

The lord is waiting to take your hand.

Vamos a programar #67 - Syntax Highlighter M (beta)

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a continuar con más sobre expresiones regulares, pero mas específicamente, cómo resaltar la sintaxis usando código en C#.

En el post anterior vimos una prueba de concepto (Proof Of Concept {POC}) de un programa que sirve para realzar la sintaxis, pero debido a varias razones que son difíciles de explicar (o porque soy flojo), no todo el código estaba implementado. Por eso hoy veremos cómo es que funciona la versión beta de este programa y explicaré un poco que hace cada función.

Antes que nada, veamos el código que hace funcionar las cosas:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;

namespace SyntaxHighlighter
{
	public partial class FrmMain : Form
	{
		private enum WordTypes : int
		{
			WType1 = 0,
			WType2,
			WType3,
			NType,
			OpType,
			SType,
			CType,
			OtherType
		}
		#region "Cargar Estilos por default"
		private Color FontColor1 = (Color)Properties.Settings.Default["ColorRW1"];
		private Color FontColor2 = (Color)Properties.Settings.Default["ColorRW2"];
		private Color FontColor3 = (Color)Properties.Settings.Default["ColorRW3"];
		private Color ColorComments = (Color)Properties.Settings.Default["ColorCom"];
		private Color ColorNum = (Color)Properties.Settings.Default["ColorNum"];
		private Color ColorStrings = (Color)Properties.Settings.Default["ColorStrings"];
		private Color ColorOperator = (Color)Properties.Settings.Default["ColorOp"];
		private Color GeneralColor = (Color)Properties.Settings.Default["GeneralFontColor"];
		private Font GeneralFont = (Font)Properties.Settings.Default["GeneralFont"];
		#endregion

	# region "Palabras reservadas"
		private string ReservedWords1 = @"\b(as|base|bool|break|byte|case|catch|char|checked|class|continue|decimal|default|delegate|do|double|else|enum|explicit|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|is|lock|long|namespace|null|object|operator|out|params|ref|return|sbyte|short|sizeof|stackalloc|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|ushort|using|var|void|while)\b";
		private string ReservedWords2 = @"\b(public|private|internal|protected|abstract|async|const|event|extern|new|override|partial|readonly|sealed|static|unsafe|virtual|volatile)\b";
		private string Comments = @"((\/\/.+)|(\/\*[\s\S]*?\*\/)|\b(\\\\)\b)";
		private string Numbers = @"((0[xX])(\d|[a-f|A-F])+)|(\b(\d+)\b)";
		private string Strings = "(\".*?\")";
		private string Operators = @"\[|\]|\(|\)|\.|\?|\b(and)\b|(\:\:)|\+|\-|\*|\/|\%|\&|\^|\!|\~|\=|\<|\>|\?\:|\++\|\-\-|\&\&|\<\<|\>\>|\=\=|\!\=|\<\=|\>\=|\+\=|\-\=|\*\=|\/\=|\%\=|\&\=|\=|\^\=|\<\<\=|\>\>\=|\-\>|\?\?|\=\>|";
	#endregion

	#region Delegados
		private delegate void UpadteRTBDelegate(int Start, int End, Color Colores);
		private delegate string GetTextDelegate();
	#endregion

		private void UpdateRTB(int Start, int End, Color Colores)
		{
			if (RTBMain.InvokeRequired)
				RTBMain.Invoke(new UpadteRTBDelegate(UpdateRTB), Start, End, Colores);
			else
			{
				RTBMain.SelectionStart = Start;
				RTBMain.SelectionLength = End;
				RTBMain.SelectionColor = Colores;
			}

		}

		private string GetText()
		{
			if (RTBMain.InvokeRequired)
				return (string)RTBMain.Invoke(new GetTextDelegate(GetText));
			else
				return RTBMain.Text;
		}

		private void DoTheWork()
		{
			Thread DoWork = new Thread(new ThreadStart(() => ColorizeAll()));
			DoWork.Start();
		}

		public FrmMain()
		{
			InitializeComponent();
		}

		private void ColorizeAll()
		{
			Colorize(ReservedWords1, WordTypes.WType1);
			Colorize(ReservedWords2, WordTypes.WType2);
			Colorize(Numbers, WordTypes.NType);
			Colorize(Operators, WordTypes.OpType);
			Colorize(Strings, WordTypes.SType);
			Colorize(Comments, WordTypes.CType);
		}

		private void SaveColor(WordTypes Tipo, Color Colores)
		{
			switch (Tipo)
			{
				case WordTypes.WType1:
					FontColor1 = Colores;
					Properties.Settings.Default["ColorRW1"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.WType2:
					FontColor2 = Colores;
					Properties.Settings.Default["ColorRW2"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.WType3:
					FontColor3 = Colores;
					Properties.Settings.Default["ColorRW3"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.NType:
					ColorNum = Colores;
					Properties.Settings.Default["ColorNum"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.OpType:
					ColorOperator = Colores;
					Properties.Settings.Default["ColorOp"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.SType:
					ColorStrings = Colores;
					Properties.Settings.Default["ColorStrings"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.CType:
					ColorComments = Colores;
					Properties.Settings.Default["ColorCom"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.OtherType:
					GeneralColor = Colores;
					RTBMain.ForeColor = GeneralColor;
					Properties.Settings.Default["GeneralFontColor"] = Colores;
					Properties.Settings.Default.Save();
					break;
				default:
					break;
			}
		}

		private void Colorize(string Patron, WordTypes Tipo)
		{
			Color CurrentFontColor = GeneralColor;
			switch (Tipo)
			{
				case WordTypes.WType1:
					CurrentFontColor = FontColor1;
					break;
				case WordTypes.WType2:
					CurrentFontColor = FontColor2;
					break;
				case WordTypes.WType3:
					CurrentFontColor = FontColor3;
					break;
				case WordTypes.CType:
					CurrentFontColor = ColorComments;
					break;
				case WordTypes.NType:
					CurrentFontColor = ColorNum;
					break;
				case WordTypes.SType:
					CurrentFontColor = ColorStrings;
					break;
				case WordTypes.OpType:
					CurrentFontColor = ColorOperator;
					break;
				case WordTypes.OtherType:
					CurrentFontColor = GeneralColor;
					break;
			}
			//Si quitas el comentario siguiente y comentas la linea que sigue, puedes ver lo importante de la funcion GetText()
			//MatchCollection wordColl = Regex.Matches(RTBMain.Text, Patron);
			MatchCollection wordColl = Regex.Matches(GetText(), Patron);
			foreach (Match m in wordColl)
			{
				UpdateRTB(m.Index, m.Length, CurrentFontColor);
			}
		}

		private void TSBtnOpen_Click(object sender, EventArgs e)
		{
			OpenFileDialog OpDiag = new OpenFileDialog();
			OpDiag.Filter = "Código fuente C#|*.cs|Arvhivos de texto|*.txt|Todos los archivos|*.*";
			OpDiag.Multiselect = false;
			DialogResult DiagRes = OpDiag.ShowDialog();
			if (DiagRes == DialogResult.OK)
			{
				RTBMain.Clear();
				StreamReader SR = new StreamReader(OpDiag.FileName, Encoding.UTF8);
				RTBMain.AppendText(SR.ReadToEnd());
				DoTheWork();

			}
		}

		private void TSBtnSave_Click(object sender, EventArgs e)
		{
			SaveFileDialog SaveDiag = new SaveFileDialog();
			SaveDiag.Filter = "Texto enriquecido|*.rtf";
			if (SaveDiag.ShowDialog() == DialogResult.OK)
				RTBMain.SaveFile(SaveDiag.FileName);
		}

		private void TSBtnFont_Click(object sender, EventArgs e)
		{
			FontDialog FontDiag = new FontDialog();
			FontDiag.ShowApply = false;
			if (FontDiag.ShowDialog() == DialogResult.OK)
			{
				RTBMain.Font = FontDiag.Font;
				Properties.Settings.Default["GeneralFont"] = FontDiag.Font;
				Properties.Settings.Default.Save();
				DoTheWork();
			}
		}

		private void TSBtnColor_Click(object sender, EventArgs e)
		{
			ColorDialog ColorDiag = new ColorDialog();
			if (ColorDiag.ShowDialog() == DialogResult.OK)
			{
				SaveColor((WordTypes)TSLBWordTypes.SelectedIndex, ColorDiag.Color);
			}

		}

		private void FrmMain_Load(object sender, EventArgs e)
		{
			RTBMain.Font = GeneralFont;
			RTBMain.ForeColor = GeneralColor;
			TSLBWordTypes.SelectedIndex = 7;
		}

		private void TSBtnReDo_Click(object sender, EventArgs e)
		{
			DoTheWork();
		}

		private void TSLBWordTypes_TextChanged(object sender, EventArgs e)
		{

		}
	}
}

La explicación de cada parte en orden de aparición.

Primero tenemos una enumeración llamada "WordTypes" que hereda del tipo "int", esto solo es para poder usar su valor de manera mas sencilla. La enumeración esta compuesta  por los elementos:

  • "WType1" - y su valor es 0
  • "WType2" - Su valor es 1
  • "Wtype3" - Su valor es 2
  • "NType" - Su valor es 3
  • "OpType" - Su valor es 4
  • "SType" - Su valor es 5
  • "CType" - Su valor es 6
  • "OtherType" - Su valor es 7
Solo para tenerlo en cuenta, "WType" es por Word Type, "NType" es por Numbers Type, "OpType" es por Operator Type, "SType" es por String Type, "CType" es por Comment Type y "OtherType" su nombre es muy explicatorio. Si bien recuerdo, en la gran mayoría de los lenguajes se cubren todos los tipos de palabras reservadas, si alguno requiriera de algún tipo más, se harán las adecuaciones para que se puedan escoger mas tipos, pero por ahora cubrimos gran parte y el principal por ahora que es C#.

Después creamos la función "UpdateRTB()", está función servirá para poder darle los estilos al texto en el control "RTBMain" y hay dos razones para usar un procedimiento para esa tarea. El primero es que esta acción se va a realizar mas de una vez y la otra; es para poder usarla desde un "Thread" (alguna vez dije que en lugar de "Thread" usaría hilo pero naahh) y por eso es que justo antes de crear el procedimiento, también creamos un delegado que contiene los mismos parámetros que la función. La función recibe tres parámetro, el primero del tipo "int", el segundo del tipo "int" y el tercero del tipo "Color", dentro de la función lo primero que revisa es si el control esta siendo invocado, si es así, manda a llamar al delegado para que este establezca los parámetros. Si hacemos un poco de memoria, en el post anterior, vimos que para resaltar una palabra usábamos tres valores (de ahí los tres parámetros), el primero era donde iba a comenzar la selección, el segundo que tanto se iba a seleccionar y el tercero era el color para el resaltado. Con las adecuaciones que hicimos, ahora podemos hacer el resaltado desde otro "Thread".

La siguiente función es "GetText()", está función tiene cómo objetivo obtener el texto del control "RTBMain" Al igual que la función anterior, primero prueba si el control está siendo invocado, si es asi la funcion llama al delegado "GetTextDelegate" y regresa un valor del tipo "string" con el texto que el delegado obtuvo, en caso contrario devuelve un valor del tipo "string" con el texto de "RTBMain"
Aquí vemos la importancia de los delgados. Ya los veremos mas adelante ;)
La siguiente función es "DoTheWork()", está función crea un "Thread" diferente y ejecuta la función "ColorizeAll()"

El siguiente procedimiento es "ColorizeAll()", esta función su trabajo es llamar a la función "Colorize", llamándola tantas veces cómo tipos de palabras reservadas tenemos, debido a cómo está hecho el programa, aquí es muy importante conservar el orden (incluso ahora dos de los elementos están al revés por lo que podrás notar que alguna cosas tienen color que no deben, eso si, nada grave).

La siguiente función es "SaveColor". Esta función recibe dos parámetros, el primero es del tipo "WorTypes" y el segundo es del tipo "Color", esta función se usa para guardar los colores que el usuario elija para cada tipo de palabra reservada, usando la instrucción "switch", comprueba de que caso se trata y dependiendo de que valor sea "Tipo"; que es del tipo "WordTypes";  y guarda el valor en la configuración del programa.

Finalmente el procedimiento "Colorize". Recibe dos parámetro, el primero del tipo "string" que es el patrón de expresión regular que se va a buscar y el segundo del tipo "WordTypes". Al igual que el procedimiento para guardar las configuraciones, en este procedimiento "probamos" cual de los posibles valores es el que se va a evaluar y a partir de ahí cambiar al color que se designó para el resaltado.

Al implementar todas la funciones, el programa ya sirve, pero aun es beta, puesto que tienen errores básicos. Todos esto los arreglaremos, pero primero trataremos de implementar las funciones que serian "de cajón", es decir resaltar la sintaxis de otros lenguajes y no solo de C#. Eso lo haremos en el siguiente post, continuaremos con mas de este programa.

Y bien, por ahora es todo, cómo de costumbre, puedes bajar el código de mi dropbox para probarlo y modificarlo.

Los leo luego.

No hay comentarios. :

Publicar un comentario

Vamos a programar #66 - Expresiones regulares - Resaltador de sintaxis (POC).

No hay comentarios.
Hola de nuevo a todos, el día de hoy vamos a continuar con un poco mas de expresiones regulares. Para seguir con el aprendizaje, veremos cómo hacer un programa en ## que servirá para resaltar la sintaxis de código fuente en C#.

El otro día mientras revisaba los mensajes de twitter, vi uno que me llamo la atencion:
Recuerden, anonimato ante todo!!.

Un usuario me pregunto si era posible crear una aplicación para resaltar la sintaxis de un código fuente y aprovechando que andamos en las expresiones regulares, dije: "por qué no?".

Para empezar, hay que recordar un poco de los fundamentos del lenguaje que queremos resaltar, por lo general está formado por componentes de la siguiente lista:

  • Palabras clave
    • Tipo uno
    • Tipo dos
    • Tipo tres
  • Números
  • Operadores
  • Cadenas de texto
  • Comentarios
En C# hay una lista de palabras reservadas, para fines prácticos, los podemos separar en instrucciones, tipos, modificadores, números, operadores, cadenas de texto y comentarios (espero no omitir nada), entonces podemos crear un expresión regular para cada grupo.
Una lista de las palabras reservadas en C# (sin separar) es esta:

  • as
  • base
  • bool
  • break
  • byte
  • case
  • catch
  • char
  • checked
  • class
  • continue
  • decimal
  • default
  • delegate
  • do
  • double
  • else
  • enum
  • explicit
  • false
  • finally
  • fixed
  • float
  • for
  • foreach
  • goto
  • if
  • implicit
  • in
  • int
  • interface
  • is
  • lock
  • long
  • namespace
  • null
  • object
  • operator
  • out
  • params
  • ref
  • return
  • sbyte
  • short
  • sizeof
  • stackalloc
  • string
  • struct
  • switch
  • this
  • throw
  • true
  • try
  • typeof
  • uint
  • ulong
  • unchecked
  • ushort
  • using
  • var
  • void
  • while
  • public
  • private
  • internal
  • protected
  • abstract
  • async
  • const
  • event
  • extern
  • new
  • override
  • partial
  • readonly
  • sealed
  • static
  • unsafe
  • virtual
  • volatile
Una vez que tenemos la lista, podemos crear una expresión regular teniendo en cuenta que son palabras exactas entonces si tomamos de la lista anterior solo los primeros tres elemento, la expresion regular queda asi

\b(as|base|bool)\b

Obviamente hay que agregar el resto de ellas para poder encontrar todas.

Para poder encontrar los números, primero tenemos que recordar que en C# se pueden escribir los numero directamente en base decimal o en base hexadecimal. Para la base hexadecimal hay que recordar que los número tendrán el formato "0x" antes de cualquier número en esa base.

Para poder encontrar los números, podemos crear una expresión regular cómo esta:

(\b(\d+)\b)

para los números decimales. Y algo cómo esta:

(0[xX])(\d|[a-f|A-F])+)

Para los números hexadecimales.

Para poder encontrar los comentarios, lo primero que haremos es recordar cómo es que forman los comentarios en c#. En C# hay al menos dos tipos de comentarios. El primero es el comentario sencillo y siempre comienza por "//" hasta el final de la linea. El segundo es el comentario multi-linea, este consiste que se comentará todo desde el inicio hasta al final (aunque parezca redundante), eso quiere decir que desde la marca de inicio "/*" sin importar que haya (nuevas lineas, apostrofes, comas, etc.) el comentario se hará hasta que se encuentre con el indicio de cierre "*/"

Para encontrar los comentarios simple podemos crear una expresión regular cómo la que sigue:

(\/\/.+)

Y para los comentario de muchas líneas, podemos crear una cómo la que sigue

(\/\*[\s\S]*?\*\/))

Para las cadenas texto hay que recordar que siempre van entre comillas " " ":

(\".*?\")

Y para los operadores en C#, la lista es la siguiente:

  • []
  • ()
  • .
  • ?
  • and
  • ::
  • +
  • -
  • *
  • /
  • %
  • &
  • ^
  • !
  • ~
  • =
  • <
  • >
  • ?:
  • ++
  • --
  • &&
  • <<
  • >>
  • ==
  • !=
  • <=
  • >=
  • +=
  • -=
  • *=
  • /=
  • %=
  • &=
  • =
  • ^=
  • <<=
  • >>=
  • ->
  • ??
  • =>

Y podemos crear un patrón cómo el que sigue (Solo para los primeros tres).

(\[|\]|\()

Ahora podemos pasar al código en C#.

/*+----------------------------------------------------------+
| Lista de palabras reservadas                               |
+------------------------------------------------------------+
| as|base|bool|break|byte|case|catch|char|checked|class      |
+------------------------------------------------------------+
| continue|decimal|default|delegate|do|double|else|enum      |
+------------------------------------------------------------+
| explicit|false|finally|fixed|float|for|foreach|goto|if     |
+------------------------------------------------------------+
| implicit|in|int|interface|is|lock|long|namespace|null      |
+------------------------------------------------------------+
| object|operator|out|params|ref|return|sbyte|short|sizeof   |
+------------------------------------------------------------+
| stackalloc|string|struct|switch|this|throw|true|try|typeof |
+------------------------------------------------------------+
| uint|ulong|unchecked|ushort|using|var|void|while           |
+----------------------------------------------------------+*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;

namespace SyntaxHighlighter
{
	public partial class FrmMain : Form
	{
		private string ReservedWords1 = @"\b(as|base|bool|break|byte|case|catch|char|checked|class|continue|decimal|default|delegate|do|double|else|enum|explicit|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|is|lock|long|namespace|null|object|operator|out|params|ref|return|sbyte|short|sizeof|stackalloc|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|ushort|using|var|void|while)\b";
		private string ReservedWords2 = @"\b(public|private|internal|protected|abstract|async|const|event|extern|new|override|partial|readonly|sealed|static|unsafe|virtual|volatile)\b";
		private string Comments = @"((\/\/.+)|(\/\*[\s\S]*?\*\/))";
		private string Numbers = @"((0[xX])(\d|[a-f|A-F])+)|(\b(\d+)\b)";
		private string Strings = "(\".*?\")";
		private string Operators = @"\[|\]|\(|\)|\.|\?|(and)|(\:\:)|\+|\-|\*|\/|\%|\&|\^|\!|\~|\=|\<|\>|\?\:|\++\|\-\-|\&\&|\<\<|\>\>|\=\=|\!\=|\<\=|\>\=|\+\=|\-\=|\*\=|\/\=|\%\=|\&\=|\=|\^\=|\<\<\=|\>\>\=|\-\>|\?\?|\=\>|";
		public FrmMain()
		{
			InitializeComponent();
		}

		private Color FontColor1 = (Color)Properties.Settings.Default["ColorRW1"];
		private Color FontColor2 = (Color)Properties.Settings.Default["ColorRW2"];
		//private Color FontColor3 = (Color)Properties.Settings.Default["ColorRW3"];
		private Color FontComments = (Color)Properties.Settings.Default["ColorCom"];
		private Color FontNum = (Color)Properties.Settings.Default["ColorNum"];
		private Color FontStrings = (Color)Properties.Settings.Default["ColorStrings"];
		private Color FontOperator = (Color)Properties.Settings.Default["ColorOp"];

		private enum WordTypes
		{
			WType1  = 0,
			WType2,
			WType3,
			CType,
			NType,
			SType,
			OpType,
			OtherType
		}

		private void ColorizeAll()
		{
			Colorize(ReservedWords1, WordTypes.WType1);
			Colorize(ReservedWords2, WordTypes.WType2);
			Colorize(Numbers, WordTypes.NType);
			Colorize(Operators, WordTypes.OpType);
			Colorize(Strings, WordTypes.SType);
			Colorize(Comments, WordTypes.CType);
		}

		private void Colorize(string Patron, WordTypes Tipo)
		{
			Color CurrentFontColor = Color.Black;
			switch (Tipo)
			{
				case WordTypes.WType1:
				CurrentFontColor = FontColor1;
				break;
				case WordTypes.WType2:
				 CurrentFontColor = FontColor2;
				break;
				case WordTypes.WType3:
				CurrentFontColor = Color.Black;
				break;
				case WordTypes.CType:
				CurrentFontColor = FontComments;
				break;
				case WordTypes.NType:
				CurrentFontColor = FontNum;
				break;
				case WordTypes.SType:
				CurrentFontColor = FontStrings;
				break;
				case WordTypes.OpType:
				CurrentFontColor = FontOperator;
				break;
				case WordTypes.OtherType:
				CurrentFontColor = Color.Black;
				 break;
			}

				MatchCollection wordColl = Regex.Matches(RTBMain.Text, Patron);
				foreach (Match m in wordColl)
				{
					RTBMain.SelectionStart = m.Index;
					RTBMain.SelectionLength = m.Length;
					RTBMain.SelectionColor = CurrentFontColor;
			}
		}

		private void TSBtnOpen_Click(object sender, EventArgs e)
		{
			OpenFileDialog OpDiag = new OpenFileDialog();
			OpDiag.Filter = "Código fuente C#|*.cs|Archivos de texto|*.txt|Todos los archivos|*.*";
			OpDiag.Multiselect = false;
			DialogResult DiagRes = OpDiag.ShowDialog();
			if (DiagRes == DialogResult.OK)
			{
				RTBMain.Clear();
				StreamReader SR = new StreamReader(OpDiag.FileName, Encoding.UTF8);
				RTBMain.AppendText(SR.ReadToEnd());
				ColorizeAll();
				
			}
		}

		private void TSBtnSave_Click(object sender, EventArgs e)
		{
			SaveFileDialog SaveDiag = new SaveFileDialog();
			SaveDiag.Filter = "Texto enriquecido|*.rtf";
			if (SaveDiag.ShowDialog() == DialogResult.OK)
			RTBMain.SaveFile(SaveDiag.FileName);
		}

		private void TSBtnFont_Click(object sender, EventArgs e)
		{
			FontDialog FontDiag = new FontDialog();
			FontDiag.ShowApply = false;
			if(FontDiag.ShowDialog() == DialogResult.OK)
				RTBMain.Font = FontDiag.Font;
		}

		private void TSBtnColor_Click(object sender, EventArgs e)
		{
			Form FrmOptions = new FrmAjustes();
			DialogResult DiagRes = FrmOptions.ShowDialog();
			if (DiagRes == DialogResult.OK)
			{
			}
		}
	}
}

Y bien, por ahora es todo, en lo mientras y para no alargar tanto el post, puedes descargar la versión beta del programas desde mi dropbox, la semana que viene, explicare cada parte del código (por ahora no, ya que mas seguro buena parte va a cambiar) y agrgare más funciones para que no solo resalte el código de c#

Los leo luego

No hay comentarios. :

Publicar un comentario

Vamos a programar #65 - Expresiones regulares en C#

No hay comentarios.
Hola de nuevo a todos el dia de hoy vamos a continuar con más sobre expresiones regulares y cómo es que podemos implementarlas en C#.

En post recientes hemos visto ejemplos de expresiones regulares, para poder ejecutarlos usamos Notepad++, pero ¿que pasa si no tienes instalado este programa o simplemente no quieres hacerlo porque ya tienes otro bloc de notas? Para eso vamos a crear un pequeño programa en C# que servira para probar las expresiones regulares.

Para empezar, veamos el siguiente código:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.IO;

namespace WordsFinder
{
	public partial class FrmMain : Form
	{
		public FrmMain()
		{
			InitializeComponent();
		}
		
		private Color FontColor = (Color)Properties.Settings.Default["ColorFont"];
		private Color BackGroundColor = (Color)Properties.Settings.Default["ColorBack"];
		
		private void ResetRTB()
		{
			RTBText.SelectAll();
			RTBText.SelectionBackColor = Color.FromName("white");
			RTBText.SelectionColor = Color.FromName("black");
		}

		private void Buscar()
		{
			if(!LBHistory.Items.Contains(TXTRegEx.Text))
				LBHistory.Items.Add(TXTRegEx.Text);
			ResetRTB();
			MatchCollection wordColl = Regex.Matches(RTBText.Text, TXTRegEx.Text);
			foreach (Match m in wordColl)
			{
				RTBText.SelectionStart = m.Index;
				RTBText.SelectionLength = m.Length;
				RTBText.SelectionBackColor  = BackGroundColor;
				RTBText.SelectionColor = FontColor;
			}
			RTBText.ScrollToCaret();
			TSSlblFound.Text = "Se encontraron: " + wordColl.Count.ToString();
		}

		private void BtnSearch_Click(object sender, EventArgs e)
		{
			Buscar();
		}

		private void BtnReset_Click(object sender, EventArgs e)
		{
			ResetRTB();
		}

		private void TSBtnOpen_Click(object sender, EventArgs e)
		{
			OpenFileDialog OpDiag = new OpenFileDialog();
			OpDiag.Filter = "Archivos de texto|*.txt|Todos los archivos|*.*";
			OpDiag.Multiselect = false;
			DialogResult DiagRes = OpDiag.ShowDialog();
			if (DiagRes == DialogResult.OK)
			{
				RTBText.Clear();
				this.Text = "Word Finder - " + OpDiag.FileName;
				StreamReader SR = new StreamReader(OpDiag.FileName, Encoding.UTF8);
				RTBText.AppendText(SR.ReadToEnd());
			}
		}

		private void TSBtnColor_Click(object sender, EventArgs e)
		{
			Form FrmOptions = new FrmAjustes();
			DialogResult DiagRes = FrmOptions.ShowDialog();
			if (DiagRes == DialogResult.OK)
			{
				FontColor = (Color)Properties.Settings.Default["ColorFont"];
				BackGroundColor = (Color)Properties.Settings.Default["ColorBack"];
			}
		}

		private void LBHistory_SelectedIndexChanged(object sender, EventArgs e)
		{
			TXTRegEx.Text = LBHistory.Items[LBHistory.SelectedIndex].ToString();
		}

		private void TXTRegEx_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (e.KeyChar == 13)
				Buscar();
		}
	}
}

Para poder usar las expresiones regulares, debemos de importar el espacio de nombres "System.Text.RegularExpressions". Luego, todo el trabajo se hace en la función "Buscar()". En esta funcion, lo primero que hacemos es revisar si la expresión que queremos buscar ya está guardada en el "ListBox" "LBHistory", si no esta la agrega; en caso contrario, no lo hace. Luego creamos una variable del tipo "MatchCollection" llamada "wordColl" cuyo valor será el resultado de la función  "Regex.Matches()", a su vez esta función recibe dos parámetros; el primero: Es un valor del tipo "string" que será la cadena de texto en la que se buscarán los patrones de expresión regular, para este parámetro simplemente asignamos el contenido de "RTBText" que es un cuadro de texto de formato enriquecido (RichTextBox). El segundo parámetro también es una cadena de texto, este debe de ser el patrón de expresión regular que se quiere evaluar, en este caso el valor "Text" del control "TXTRegEx" (TextBox).

Una vez que se ejecuta la función, todos los resultados se "guardarán" en la variable "wordColl". Para poder saber cuales fueron los resultados, debemos de recorrer los valores, para eso, podemos usar un iteardor "foreach" recorriendo todos los valores. Cada uno de estos será un valor del tipo "Match" y  cómo lo sugiere su nombre, es el singular de "MatchCollection". Cada valor del tipo "Match" posee sub-funciones, en este caso solo usamos "Match.Index()" que sirve para indicar en que posición se encontró la coincidencia dentro de la cadena de texto que se pasó por parámetro a Regex.Matches()". este valor se usa para iniciar la selección en el control "RTBText" y luego usamos "Match.Length()"; que indica cual es la longitud de la coincidencia; para poder resaltar la palabra que se encontró en el texto del control "RTBText".



Con la anterior, se marcará cada coincidencia, pero ademas la aplicación dispone de un par de funciones adicionales, una es para abrir un archivo de texto externo y otra para poder configurar cómo es que queremos que el texto se resalte. Para eso hacemos uso de un segundo formulario cuyo código es el que sigue:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WordsFinder
{
	public partial class FrmAjustes : Form
	{
		public FrmAjustes()
		{
			InitializeComponent();
		}

		private void BtnFontColor_Click(object sender, EventArgs e)
		{
			ColorDialog ColorDiag = new ColorDialog();
			DialogResult DiagRes = ColorDiag.ShowDialog();
			if (DiagRes == DialogResult.OK)
			{
				Properties.Settings.Default["ColorFont"] = ColorDiag.Color;
				lblMuestraBig.ForeColor = ColorDiag.Color;
				Properties.Settings.Default.Save();
			}
		}

		private void BtnAccept_Click(object sender, EventArgs e)
		{
			this.DialogResult = DialogResult.OK;
			this.Close();
		}

		private void BtnBackColor_Click(object sender, EventArgs e)
		{
			ColorDialog ColorDiag = new ColorDialog();
			DialogResult DiagRes = ColorDiag.ShowDialog();
			if (DiagRes == DialogResult.OK)
			{
				Properties.Settings.Default["ColorBack"] = ColorDiag.Color;
				lblMuestraBig.BackColor = ColorDiag.Color;
				Properties.Settings.Default.Save();

			}

		}

		private void BtnCancel_Click(object sender, EventArgs e)
		{
			this.DialogResult = DialogResult.Cancel;
			this.Close();
		}

		private void FrmAjustes_Load(object sender, EventArgs e)
		{
			lblMuestraBig.BackColor = (Color)Properties.Settings.Default["ColorBack"];
			lblMuestraBig.ForeColor = (Color)Properties.Settings.Default["ColorFont"];
		}
	}
}



La Aplicación es sencilla y no hace uso de todas las herramientas disponibles en la clase "Regex", pero para probar los ejemplos sirve bien. Cómo de costumbre el código lo puedes descargar de mi dropbox para poder probarlo.

Y bien, por ahora es todo, recuerda que los ejemplos de expresiones regulares que hemos visto en post anteriores, los puedes probar en este programa, aun no es a prueba de errores, pero eso lo veremos mas adelante.

Los leo luego.

No hay comentarios. :

Publicar un comentario