PDA

Ver la Versión Completa : Búsqueda muy específica en un string...


JuanBCT
22-04-2005, 17:12:42
Hola! Les quería comentar acerca de un problemita que tengo, a ver si alguien me puede ayudar...

Hice una conversión de un RTF de un DBRichEdit a HTML, con una librería freeware que encontré por ahí. Me dí cuenta que tiene un par de errores, por lo que una vez que la grabo la cargo en un RichEdit auxiliar y la recorro corrigiendo errores.
Creía que ya lo tenía pero me dí cuenta que si uno quiere comenzar una línea con un espacio, a leer el HTML éste es obviado.

O sea, si yo tengo:
<B><I><U><FONT COLOR="#000000"><FONT FACE="Tahoma">
<FONT SIZE=-2> Sector Programación</FONT>
</FONT></FONT></U></I></B><BR>
...me va a tirar la frase "Sector programación" totalmente alineada a la izquierda, a pesar del espacio entre ">" y "S".

Averiguando, encontré que si uno reemplaza dicho espacio con "&nbsp", se convierte en un espacio tenido en cuenta. El código quedaría así:
<B><I><U><FONT COLOR="#000000"><FONT FACE="Tahoma">
<FONT SIZE=-2>&nbspSector programación</FONT>
</FONT></FONT></U></I></B><BR>
Bien, ahora lo que yo pretendo es corregir esto por código; cargo cada línea como un String y busco, el tema es que si pongo "reemplazar cada ' ' por '&nbsp'; se metería por ejemplo entre FONT COLOR, FONT FACE, etc... por lo que dejaría de funcionar bien el código. Esto lo haría así:
SS:=StringReplace(SS, ' ','&nbsp', [rfReplaceAll, rfIgnoreCase]);

Mi pregunta es: ¿cómo hago para realizar el reemplazo anterior pero limitando la búsqueda sólo en el texto? Estaba pensando que puedo hacer algo como "buscar sólo en lo que haya entre un > y un <", ya que salvo el texto en sí en todo lo demás que cumple esa condición no hay nada....

Bueno, espero que alguien me pueda ayudar; disculpen que el mensaje sea tan largo! Traté de ser lo más específico posible...
Un saludo, y desde ya muchas gracias.

Juan

Ing_Fajardo
22-04-2005, 17:27:25
Lo primero que te recomendaria es conseguir otro vcl que te ayude con esto.

Si no tienes de otra, pues ni modo, por programacion tienes que sustituir los espacios siempre sean etiquetas que aparecerán en las paginas.

Saludos.

Lepe
22-04-2005, 17:42:31
Basicamente puedes usar 2 variables:
- PosMayor (posición del simbolo Mayor)
- PosMenor, (posición del simbolo menor)

Junto con la funcion Pos de delphi.
en un Stringlist cargas toda la pagina web


stringactual :='< FONT SIZE=-2 >Sector programación< /FONT>';

PosMayor := pos('>',stringactual);
PosMenor := pos('<',stringactual);
while (PosMayor < PosMenor) and (trim(copy(stringactual,posMayor, posMenor-PosMayor))<>'' do
begin
// insertas el "&nbsp", justo despues de PosMayor
// guardas la cadena modificada en otra variable.
// stringactual := copy(stringactual,posMenor,255);
// seguimos con el resto de la cadena
end


Useasé, entre un carácter '>' y otro '<' debe existir alguna letra, y además distinto de espacios vacios.

Deberás perfilarlo un poco más, quitando saltos de linea si existen....pero creo que te servirá.

Un saludo

Lepe
22-04-2005, 17:52:10
Si no tienes de otra, pues ni modo, por programacion tienes que sustituir los espacios siempre sean etiquetas que aparecerán en las paginas.

Saludos.

Pues creo que si se puede hacer, ya que todos los códigos de control, van
encerrados justamente al contrario de lo que se busca.

Ejemplo:

<FONT COLOR="#000000"> no entraría en el If por la primera condición.

><I><U> tampoco entra,
por la segunda condición (no hay caracteres entre los 2 primeros símbolos).
Aqui tienes que perfilar la rutina, para que quite los dos caracteres iniciales y
siga analizando la misma cadena




Saludos

mamcx
22-04-2005, 18:10:32
Te recomiendo ABSOLUTAMENTE mirar la posibilidad de usar Expresiones Regulares... que son basicamente, instrucciones muy poderosas de busqueda y reemplazo de cadenas.

Por ejemplo, ayer necesitaba pasar todos los codigos que eran

i := 0;
while i < Algo.Count do

por

for i:=0 to Algo.Count-1 do

y con expresiones regulares se hacen en dos-3 lineas de codigo. Especialmente al lidiar con HTML son muy valiosas y de hecho, una razon por la cual es popular PHP es al soporte nativo que le da a esto.

Sin esta ayuda, la veo dificil, porque lo que necesitas es armar un parser o meter mucha logica para cubrir todos los casos...

Puedes ver una lista de funciones prefabricadas en http://www.regexlib.com/. Si te suena la idea bajate el programa "The Regulator" es gratuito y sirve para armar las expresiones.

Sin embargo, ten en cuenta que es un poco dificil de entender al principio y que necesitar bajarte una libreria de estas para Delphi (porque no la trae nativamente)...

Pero te recomiendo esta ruta, porque el problema que estas teniendo, no te preocupes, saldran mas ;)

roman
22-04-2005, 18:22:57
Hoy vengo de pesimista y digo que esto es una tarea cuasi imposible. :D

Para empezar aclaremos que no se trata de un error de la librería mencionada sino que es inherente a la naturaleza del HTML.

Por otra parte, aún evitando colocar la secuencia &nbsp; en los espacios entre parámetros de una etiqueta, ¿qué pasa con todos los demás espacios? Los espacios normales entre palabras. Dirán ustedes, bueno, ¡qué importa! de cualquier manera son espacios que deben aparecer en la representación visual. Sí, pero además de saturar el archivo HTML con caracteres innecesarios con el consecuente aumento en el tiempo de descarga, no olvidemos que &nbsp;viene de non breaking space, es decir un espacio que impedirá que se corte la línea de salida, así que todo el contenido del archivo HTML se verá en una sóla línea (al menos grandes partes hasta no encontrarse una etiqueta que obligue al cambio de párrafo como <p>).

Digo cuasi imposible porque todo es posible, pero lo veo muy difícil tan sólo con las herramientas de Delphi. La mejor opción será la que (ahora veo que ya lo indica mamcx) el uso de un analizador de expresiones regulares (y el estudio de éstas, pues no creo que la expresión regular necesaria sea tan fácil)

// Saludos

JuanBCT
22-04-2005, 19:47:26
Hola, les agradezco por responder tan rápido; si bien no solucioné el problema me voy orientando más...

Por otra parte, aún evitando colocar la secuencia &nbsp; en los espacios entre parámetros de una etiqueta, ¿qué pasa con todos los demás espacios? Los espacios normales entre palabras. Dirán ustedes, bueno, ¡qué importa! de cualquier manera son espacios que deben aparecer en la representación visual. Sí, pero además de saturar el archivo HTML con caracteres innecesarios con el consecuente aumento en el tiempo de descarga, no olvidemos que &nbsp;viene de non breaking space, es decir un espacio que impedirá que se corte la línea de salida, así que todo el contenido del archivo HTML se verá en una sóla línea (al menos grandes partes hasta no encontrarse una etiqueta que obligue al cambio de párrafo como <p>).
No podría hacer algo así: "Ubicar lo que está después de un > HASTA el 1er caracter <> a ' ' Y de longitud>0"

Luego, esto lo paso a otro string (ej: STEMP) y ejecuto:

STEMP:=StringReplace(STEMP, ' ','&nbsp', [rfReplaceAll, rfIgnoreCase]);

... y esto que obtengo lo reemplazo en el string que leí originalmente...

Creo que de esta manera: 1) Evito poner los &nbsp en los espacios entre las etiquetas
2) Evito poner &nbsp innecesarios, porque teóricamente el string temporal tendría que estar cargado con espacios, o nada si el texto está totalmente contra el margen....

Bueno, voy a ver si lo pruebo; les voy a agradecer cualquier comentario/ayuda al respecto...

Saludos!!!

Elfoscuro
22-04-2005, 21:47:24
Yo lo que haría sería:

Lees y si encuentras un '<', avanzas hasta que encuentres un '>'.

Empiezas a pasar chars a una string temporal hasta que vuelvas a encontrar un '<'.

Si la cadena es diferente de '', entonces miro el primer char, y si es espacio, lo sustituyo.

******** Pregunta: la cadena '&nbsp; algo' ¿ya serviría? Es decir, que si el primer char no es un espacio, ¿el HTML muestra lo que haya, íntegro?

Después añades a la salida la cadena temporal y sigues.

Un saludo

mamcx
22-04-2005, 22:08:25
A lo que vengo es que estas es haciendo un PARSER. Si eso es complicado, de hacerlo manualmente.

El punto es que el HTML (asi como el C++) son muy dificiles de parsear. Hay muchas formas en como las supociciones se convierten en problemas. Nada te garantiza que el html siga exactamente lo que uno piensa. Aun cuando un programa lo genera, vaya uno a saber si DOS veces y SIEMPRE hara lo mismo. En el momento que pase:


<FONT SIZE=-2 bgcolor=FFFF> //otros atributos
Sector programación</FONT>

<FONT SIZE=-2> Sector programación</FONT> //dos espacios

<FONT SIZE=-2>
Sector programación</FONT>

<FONT SIZE=-2>
Sector > programación</FONT>

<FONT SIZE=-2>&nspSector programación</FONT> //meteria un espacio mas

<FONT> </FONT> //una etiqueta vacia
<FONT> Sector >programación <-- comentario html --/></FONT>



y aunque en fin, tal vez me estoy imaginando los problemas, en armar parsers SOLO se deben cubrir los casos VALIDOS y olvidar los invalidos.

De hecho, la logica de lo que piensas hacer es mas o menos:

Detectar las etiquetas FONT, ir al caracter de cierre, mirar si hay espacios, insertar los caracteres.

Me tome la molestia de armar una expresion regular y ademas te la explico:


\<FONT(?<atributos>.*)\>(?<espacio>\s)+(?<titulo>.+)\<\/FONT\>



\< = detectar a "<" tal cual (es como evitar que se cojan los comandos / caracteres reservados del lenguaje de expresiones regulares

FONT = Detectar la secuencia exacta de caracteres FONT

(?<atributos>.*) = Realizar una captura con el nombre atributos
.* =Cualquier secuencia de caracteres con espacios y todo

\> = detectar a ">" tal cual

(?<espacio>\s) = Realizar una captura con el nombre espacio
\s = Detecta un espacio

+ = La captura anterior, ejecutarla por lo menos por un espacio detectado hasta cuantos espacios existan

(?<titulo>.+) = Realizar una captura con el nombre titulo
.+ = Cualquier secuencia de caracteres que sea encontrada AL MENOS una vez


Como vez, un monton de logica de programacion condensada en una linea de codigo

Para reemplazar:


<FONT ${atributos}>&nbsp${titulo}</FONT>


Esta expresion cubre MUCHAS mas posibilidades que el codigo que estabas planteando, aunque de los ejemplos que te mostre no cojeria si hay un ENTER entre los caracteres del titulo. Por ejemplo captura


<FONT SIZE=-2> Sector programación/FONT>
<FONT SIZE=-2> Sector programación/FONT>
<FONT> Sector programación/FONT>
<FONT> Sector >programación</FONT>


pero no:


<FONT SIZE=-2 bgcolor=FFFF>&nbspSector programación/FONT>
<FONT
<FONT SIZE=-2 bgcolor=FFFF> Sector programaciónFONT>
<FONT>Sector programación</FONT> //Si quieres que esta la coja, cambia + por * despues de captura con nombre espacio


Todavia quedan varios casos por fuera, pero es la idea...

Al González
25-04-2005, 03:34:54
¡Hola a todos!

Sin duda un asunto relativamente complejo, más no debemos complicarnos.

...Pregunta: la cadena '&nbsp; algo' ¿ya serviría? Es decir, que si el primer char no es un espacio, ¿el HTML muestra lo que haya, íntegro?...
Tengo esa misma duda, es decir, si tenemos una expresión HTML como:

<Etiqueta> Cien Años De Soledad </Etiqueta>

cuyo valor interno, « Cien Años De Soledad », tiene dos espacios de prefijo y dos de sufijo, ¿bastaría dejarla así?:

<Etiqueta>&nbsp; Cien Años De Soledad &nbsp;</Etiqueta>

¿o sería necesario dejarla así?:

<Etiqueta>&nbsp;&nbsp;Cien Años De Soledad&nbsp;&nbsp;</Etiqueta>

Me inclino a pensar que con un sólo «&nbsp;» de cada lado bastaría, como en la primera opción.

Haré una prueba y enseguida regreso.

Un favor: ¿podrían insertar algunos saltos de línea en sus mensajes para mejor apreciación del tema en resolución de 800 X 600 pixeles? Gracias.

Al González. :)

Al González
25-04-2005, 06:42:43
¡Hola de nuevo!

Comprobé que si es necesario un «&nbsp;» por cada espacio del prefijo y del sufijo del valor String que se encuentra entre etiquetas.

Juan:

Desarrollé una función de nombre HTMLEspaciado que nos permite solucionar este tipo de casos. He aquí el código:

...
implementation

{$R *.dfm}

Uses
StrUtils;

Function HTMLEspaciado (Const Cadena :String;
Const IndicadorTemporal :Char = #255) :String;
Var
Incremento, Indice, Ultimo :Integer;
CaracterEtiqueta :Char; { Carácter de Etiqueta }

Procedure Espaciar;
Var
CaracterBuscado :Char; { Carácter Buscado }
Begin
CaracterBuscado := CaracterEtiqueta;

Repeat
If Result [Indice] = CaracterBuscado Then
If CaracterBuscado = CaracterEtiqueta Then
CaracterBuscado := ' '
Else
Result [Indice] := IndicadorTemporal
Else
If CaracterBuscado = ' ' Then
CaracterBuscado := CaracterEtiqueta;

Inc (Indice, Incremento);
Until Indice = Ultimo;
End;
Begin
Result := Cadena;

If Result = '' Then
Exit;

CaracterEtiqueta := '>';
Indice := 1;
Incremento := 1;
Ultimo := Length (Cadena);
Espaciar;
CaracterEtiqueta := '<';
Indice := Length (Cadena);
Incremento := -1;
Ultimo := 1;
Espaciar;
Result := ANSIReplaceStr (Result, IndicadorTemporal, '&' + 'nbsp;');
End;

procedure TForm1.Button1Click(Sender: TObject);
begin
Edit2.Text := HTMLEspaciado (Edit1.Text);
end;
...

NOTA: En la línea «Result := ANSIReplaceStr...» separé la expresión '&nbsp;' en dos partes, para evitar un problema de interpretación HTML encontrado al exponer dicha expresión en formato de código Delphi. Para su utilización real no es necesario que dicha expresión esté separada.

La función HTMLEspaciado asume que todos los símbolos de menor que (<) y mayor que (>) de la cadena dada son delimitadores de etiquetas HTML, y ninguno de ellos parte de un valor que literalmente contenga esos símbolos, ya que para ello se utilizan los códigos especiales «&lt;» y «&gt;».

Realicé varias pruebas. En una de ellas introduzco la cadena '<Etiqueta de prueba> Cien Años De Soledad </Etiqueta de prueba>' en Edit1, con dos espacios a cada lado de la subcadena 'Cien Años De Soledad'. Al oprimir el botón Button1, queda en Edit2 la cadena resultante '<Etiqueta de prueba>&nbsp;&nbsp;Cien Años De Soledad&nbsp;&nbsp;</Etiqueta de prueba>'.

Es posible que tengamos que hacer más pruebas, o añadir funcionalidad extra a la rutina, pero creo que por lo pronto tenemos un avance importante.

Estoy considerando incluir esta función en la biblioteca Interfaz GH, porque me parece una necesidad relativamente común esto de "espaciar" valores HTML.

Espero esto sea de utilidad, seguimos en contacto.

Al González. :)

Al González
28-04-2005, 19:48:18
¡Hola a todos!

Juan:

Me quedé con la duda de si te sirvió la función que escribí, o de qué forma solucionaste el problema planteado. ¿Podrías compartir con nosotros tus avances al respecto? Por favor.

Gracias.

Seguimos en contacto.

Al González. :)

JuanBCT
29-04-2005, 22:13:44
Hola Al! Ante todo, mil disculpas a todos por lo que demoré en responder.
Traté de usar tu función pero no me funcionó, creo que porque uso Delphi 5 (no me encontraba la librería strutils; la q descubrí que se halla en las librerías rxlib - pero la versión de Delphi 5 de estas no traían la procedure de ANSIReplaceStr).
Igualmente, gracias a las ideas q uds. aportaron, se me ocurrió hacerlo de esta forma:

1) Leo cada línea del HTML con un for, y busco caracter='>' y caracter[i+1]=' '.
2) En una variable guardo la posición donde se encuentra el primer ' ' (la llamé istart) y voy buscando a ver si los siguientes también son espacio, cada vez q pasa guardo la posición en iend (al final va a tener la posición del último espacio antes de la primer letra).
3) Corto el string en dos, y los guardo en archivos temporales: Uno es desde el principio del string hasta la posición del último caracter antes del espacio, otro es desde el último espacio hasta el final del string ( Length(String) ).
4) Me olvidé de decir antes que guardé la cantidad de espacios en otra variable, asi que hago lo siguiente: En otro string mas [i]pego la primer parte, luego inserto tantos '&nbsp' como el contador de espacios tenga, luego pego la segunda parte que tenía cortada.

Es básicamente eso. Seguramente se puede hacer de forma más eficiente, sin tantos strings temporales y código; pero bueno, lo fuí haciendo sobre la marcha, funciona bastante rápido.... aparte soy medio cavernícola para programar!!! :)

Bueno, les muestro la parte del código que hice, les agradezco infinitamente a todos por su tiempo.
Aquí va:

//Archiv es el stringlist en donde cargué el HTML
SS:=archiv.Strings[A];
//==========================================================================
// :: RUTINA - AGREGADO DE "&nbsp"
//Busco los primeros espacios luego del > que no tenga como próximo un <, y
//reemplazo por &nbsp
//==========================================================================
//1) Voy a tener dos variables integer: istart, iend. Ahí voy a guardar las
// posiciones dentro del string donde empiezan los espacios en la línea
// y donde terminan.
// Entonces: Leo y pregunto si el caracter es ">":
//==========================================================================
for indicess:=0 to Length(SS) -1 do
begin
if (SS[indicess]='>') then
begin
if (SS[indicess+1]=' ') then
begin
istart:=indicess;
indice2:=indicess;
//Paso a usar otro indice, "indice2" para localizar donde empieza el caracter
while indice2<>iend do
begin
indice2:=indice2 + 1;
if (SS[indice2]<>' ') then
begin
iend:=indice2;
end;
end;
end;
//Ahora, a partir de que encontré el primer espacio y grabé el comienzo;
//sigo leyendo y grabo el final cada vez que encuentre otro espacio...
end;
end;
//==========================================================================
//2)Ok, después de todo esto me quedo con dos valores, ISTART e IEND.
//LA CANTIDAD DE ESPACIOS QUE TENGO QUE INSERTAR ES (IEND-ISTART-1)
//Calculemosla:
cantinst:=(iend-istart-1);
//Bien, ahora insertemos a partir del istart la cantidad cantinst de &nbsp
//O más facil aún: Copio el string desde el principio hasta ISTART y la
//grabo en un string temporal, STA. También copio el string desde IEND hasta
//Length(SS) y la paso a STB.
//Ahora, armo STFINAL así: Copio STA
// Concateno '&nbsp' CANTINST veces
// Concateno STB
// Y listo!
// Entonces:
sta:=Copy(SS,0,istart);
stb:=Copy(SS,iend,Length(SS));
stfinal:=sta;
for indice3:=0 to cantinst -1 do
begin
stfinal:=stfinal+'&nbsp';
end;
stfinal:=stfinal + stb;
SS:=stfinal;

Saludos!!!!!!

Al González
29-04-2005, 23:07:12
¡Buen día a todos!

...Traté de usar tu función pero no me funcionó, creo que porque uso Delphi 5 (no me encontraba la librería strutils; la q descubrí que se halla en las librerías rxlib - pero la versión de Delphi 5 de estas no traían la procedure de ANSIReplaceStr).
Hice la función HTMLEspaciado utilizando Delphi 7, versión donde se encuentra de forma nativa una unidad llamada StrUils, la cual contiene a la función ANSIReplaceStr.

Para compilarlo en Delphi 5, creo que bastaría susituir la llamada a ANSIReplaceStr por alguna otra función que sirva para reemplazar subcadenas, como StringReplace (no recuerdo si viene en Delphi 5) o alguna otra.

...Seguramente se puede hacer de forma más eficiente, sin tantos strings temporales y código...
Así es, con HTMLEspaciado (con las adaptaciones necesarias para Delphi 5, si son tan amables de ayudarnos los demás compañeros del foro ;)), por poner un ejemplo.

Gracias por mantenernos informado.

¡Un abrazo!

Al González. :)

P.D. ¿Podrían insertar algunos saltos de línea para leer adecuadamente este hilo en resolución de 800 X 600 pixeles? Gracias.

mamcx
29-04-2005, 23:14:48
Te resulto muy dificil lo de las expresiones regulares? Era solo cuestion de bajar un componente y dos lineas/tres a lo sumo de codigo.... y cubre mas casos que las funciones a mano expuestas...

JuanBCT
02-05-2005, 14:58:00
Hola Mario!

Como lo tenía mas o menos a medio hacer no consideré lo de las expresiones regulares, ahora que tengo un poco de tiempo lo voy a probar...
Después te comento como me fué.

Gracias, y saludos!