Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Contar letras en un TMemo (https://www.clubdelphi.com/foros/showthread.php?t=39871)

egostar 31-01-2007 22:04:53

Contar letras en un TMemo
 
Hola amigos,

Tengo un pequeño problema al contar las letras dentro de un memo.

Tengo dos alternativas, una simple y otra menos ortodoxa.

Código Delphi [-]
// Aqui la simple
procedure TForm1.Memo1Change(Sender: TObject);
begin
  StaticText1.Caption := inttostr(Length(Memo1.Lines.Text));
end;

Código Delphi [-]
//Aqui la no muy Ortodoxa
procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  Case Key of
     8,46  : begin
          If Letras > 0 then begin
             Letras := Letras-1;
             StaticText1.Caption := inttostr(Letras);
          end;
     end;
     else begin
          case Key of
             13,16,20,35,36,37,38,39,40 : begin
             end;
             else begin
                    Letras := Letras+1;
                    StaticText1.Caption := inttostr(Letras);
             end;
          end;
     end;
  end;
end;

Mi problema existencial es que no quiero que cuente los enter en el memo solo las letras, numeros y espacios.

En la primera opcion que es la simple me cuenta los enter para sumar o para restar

En la segunda opcion no me suma los enter pero si los resta.

¿Hay algo que pueda hacer para que no me cuente los Enter?

Gracias anticipadas.

Caral 31-01-2007 22:18:52

Hola egostar
No se a que te refieres con los enter, supongo que a los espacios vacios entre las letras.
No se pero se me ocurre que si es text, por que no definir un:
Código Delphi [-]
If X = ' ' then
//otra cosa
Osea que si el espacio esta vacio no lo lea.
No se se me ocurre.
Saludos

egostar 31-01-2007 22:28:51

Hola Caral, gracias por tu interes.

Me refiero a los ENTER que son los saltos de linea, no a los espacios entre palabras, esos si los estoy contando.

Saludos.

Bicho 31-01-2007 22:34:04

Hola, quizá una función como esta te sirva:

Código Delphi [-]
 function Palabras(Cadena:string):integer;
 var
    n:integer;
    AntEspacio:boolean;
 begin
   AntEspacio:=FALSE;
   If Cadena='' then Result:=0 else Result:=1;
   for n:=1 to Length(Cadena) do
   begin
     if AntEspacio and
        (Cadena[n]<>' ')and
        (Cadena[n]<>#13)and
        (Cadena[n]<>#10)
                   then Inc(Result);

     AntEspacio:=(Cadena[n]=' ') or
                 (Cadena[n]=#13) or
                 (Cadena[n]=#10);
   end;
 end;

Saludos

ArdiIIa 31-01-2007 22:36:26

Veamos


Código Delphi [-]
procedure TForm1.ContarLetras(Sender: TObject);
Var
I,Z: Integer;
cTemp :  String;
begin
Z := 0;
cTemp := Memo1.Lines.Text;
For I := 1 To  Length(cTemp) DO
if ord(cTemp[i]) < 32 then
inc(Z);

ShowMessage( IntToStr( Length(cTemp) - Z ));

end;

seoane 31-01-2007 22:42:45

Volviendo al método numero uno, el ortodoxo :p

Supongo que esto funcionaría:
Código Delphi [-]
procedure TForm1.Memo1Change(Sender: TObject);
var
  Str: String;
  i,j: integer;
begin
  Str:= Memo1.Lines.Text;
  j:= 0;
  for i:= 1 to Length(Str) do
    if (Str[i] <> #13) and (Str[i] <> #10) then
      inc(j);
  StaticText1.Caption:= IntToStr(j);
end;

Aunque supongo que ya habrías pensado en una solución parecida, entonces me pregunto si lo que te preocupaba es el tiempo que se tarda en contar los caracteres. Si es así lo podemos afinar un poco mas:
Código Delphi [-]
function Longitud(Str: PChar): Integer;
asm
        MOV     Result,0
        DEC     EAX
@@SIG:
        INC     EAX
        CMP     BYTE PTR [EAX],0
        JE      @@SALIR
        CMP     BYTE PTR [EAX],13
        JE      @@SIG
        CMP     BYTE PTR [EAX],10
        JE      @@SIG
        INC     Result
        JMP     @@SIG
@@SALIR:
end;

procedure TForm1.Memo1Change(Sender: TObject);
begin
  StaticText1.Caption:= IntToStr(Longitud(PChar(Memo1.Lines.Text)));
end;

En cuanto al segundo método, es complicado, piensa que cuando usas Enter se introducen dos caracteres y no uno, que puedes seleccionar y borrar mas de un carácter a la vez y ya no hablamos si utilizas el portapapeles ...

egostar 31-01-2007 22:46:37

Muchas gracias ArdiIIa

Quedo de 10

Aqui pongo como quedó el código completo espero que sirva para futuras consultas.

Código Delphi [-]
unit UContadorLetras;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    StaticText1: TStaticText;
    Label1: TLabel;
    BitBtn1: TBitBtn;
    procedure FormShow(Sender: TObject);
    procedure Memo1Change(Sender: TObject);
    procedure ContarLetras;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Letras: Integer;

implementation

{$R *.DFM}

procedure TForm1.ContarLetras;
Var
  I,Z: Integer;
  cTemp :  String;
begin
  Z := 0;
  cTemp := Memo1.Lines.Text;
  For I := 1 to  Length(cTemp) do
      if ord(cTemp[i]) < 32 then
         inc(Z);
  StaticText1.Caption := IntToStr(Length(cTemp) - Z);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  Letras := 0;
  StaticText1.Caption := inttostr(Letras);
end;

procedure TForm1.Memo1Change(Sender: TObject);
begin
  ContarLetras;
end;

end.

Saludos y nuevamente gracias.

egostar 31-01-2007 22:48:12

Vaya

Tambien este funcionó a la perfeccion.

Código Delphi [-]
unit UContadorLetras;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    StaticText1: TStaticText;
    Label1: TLabel;
    BitBtn1: TBitBtn;
    procedure FormShow(Sender: TObject);
    procedure Memo1Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Letras: Integer;

implementation

{$R *.DFM}

procedure TForm1.FormShow(Sender: TObject);
begin
  Letras := 0;
  StaticText1.Caption := inttostr(Letras);
end;

procedure TForm1.Memo1Change(Sender: TObject);
var
  Str: String;
  i,j: integer;
begin
  Str:= Memo1.Lines.Text;
  j:= 0;
  for i:= 1 to Length(Str) do
    if (Str[i] <> #13) and (Str[i] <> #10) then
      inc(j);
  StaticText1.Caption:= IntToStr(j);
end;

end.

Muchas gracias seoane.

Caral 31-01-2007 22:55:36

Hola, Bueno, los dos funcionan muy bien y hacen casi lo mismo, pero:
Me he quedado con que cuentan tambien los espacios en blanco, como hacer para que no los cuente.
Osea contar letras, pues que cuente letras, no espacios entre ellas, al igual que no cuenta el enter o salto de renglon.
Saludos

egostar 31-01-2007 22:57:32

Perdon Bicho pero no vi tu post antes de contestar a los demas, gracias por el aporte.

Saludos.

egostar 31-01-2007 23:01:04

Cita:

Empezado por seoane
Aunque supongo que ya habrías pensado en una solución parecida, entonces me pregunto si lo que te preocupaba es el tiempo que se tarda en contar los caracteres. Si es así lo podemos afinar un poco mas:

Realmente no me preocupa el tiempo en el conteo, incluso es imperceptible, de todos modos estoy analizando tu código ASM que me parece muy interesante aunque un poco fuera de mi alcance en este momento:D.

Saludos.

ArdiIIa 31-01-2007 23:01:05

Cita:

Empezado por Caral
Hola, Bueno, los dos funcionan muy bien y hacen casi lo mismo, pero:
Me he quedado con que cuentan tambien los espacios en blanco, como hacer para que no los cuente.
Osea contar letras, pues que cuente letras, no espacios entre ellas, al igual que no cuenta el enter o salto de renglon.
Saludos

Código Delphi [-]
procedure TForm1.ContarLetras;
Var
  I,Z: Integer;
  cTemp :  String;
begin
  Z := 0;
  cTemp := Memo1.Lines.Text;
  For I := 1 to  Length(cTemp) do
      if ord(cTemp[i]) < 33 then
         inc(Z);
  StaticText1.Caption := IntToStr(Length(cTemp) - Z);
end;

egostar 31-01-2007 23:05:03

Cita:

Empezado por Caral
Hola, Bueno, los dos funcionan muy bien y hacen casi lo mismo, pero:
Me he quedado con que cuentan tambien los espacios en blanco, como hacer para que no los cuente.
Osea contar letras, pues que cuente letras, no espacios entre ellas, al igual que no cuenta el enter o salto de renglon.
Saludos

Usando el mismo procedimiento de seoane, aqui esta para no contar espacios tampoco

Código Delphi [-]
procedure TForm1.Memo1Change(Sender: TObject);
var
  Str: String;
  i,j: integer;
begin
  Str:= Memo1.Lines.Text;
  j:= 0;
  for i:= 1 to Length(Str) do
    if (Str[i] <> #13) and (Str[i] <> #10) and (Str[i] <> #32) then
      inc(j);
  StaticText1.Caption:= IntToStr(j);
end;

Saludos

Caral 31-01-2007 23:09:18

Hola ArdiIIa
Perdona, pero lo haces facil.:D
Como buen aprendiz, no me quedo solo con el cambio de 32 x 33
Por que?
a que se debe?
tiene relacion con la cantidad de letras del abecedario?
El #13 es enter
El #33 es Espacio?
Me lo explicas, por favor.:D
Saludos

Caral 31-01-2007 23:14:43

Interesante, las dos opiones.
Varian en muy poco, pero tienen el mismo efecto.
No se para que me pueda servir el codigo, pero una cosa si tengo clara.
Aqui se aprende muchisimo y eso que no hice yo la pregunta.:D
Saludos Maestros

egostar 31-01-2007 23:21:20

Cita:

Empezado por Caral
Interesante, las dos opiones.
Varian en muy poco, pero tienen el mismo efecto.
No se para que me pueda servir el codigo, pero una cosa si tengo clara.
Aqui se aprende muchisimo y eso que no hice yo la pregunta.:D
Saludos Maestros

Pues mira, te comento para que se va a usar, tengo un cliente que escribe articulos para una revista, resulta que la editora le pide que sean de una cantidad de caracteres máxima, entonces con ese código no tiene que estar contando letra por letra cuantas lleva, una vez que termine con un botón lo quiere mandar por correo, pero eso ya es otra historia que solucionaré con Indy.

Bueno a grandes rasgos para eso lo requeria.

Saludos y gracias por el interes y sobre todo por la rapidez de sus respuestas.

Caral 31-01-2007 23:25:28

Hola
Me imagine que por ai podia andar, pero pense en una aplicacion de tipo didactica, para estudiantes, creia que podia ser para algo asi.
Pero no deja de ser muy interesantes las posivilidades del codigo de estos maestros.
Saludos

ArdiIIa 31-01-2007 23:27:23

Cita:

Empezado por Caral
Hola ArdiIIa
Perdona, pero lo haces facil.:D
Como buen aprendiz, no me quedo solo con el cambio de 32 x 33
Por que?
a que se debe?
tiene relacion con la cantidad de letras del abecedario?
El #13 es enter
El #33 es Espacio?
Me lo explicas, por favor.:D
Saludos

Está claro, es el código ASCII de cada letra, ESPACIO = 32
luego si le dices que incremente Z cada vez que el caracter sea inferior a 32, el resultado es lo que se esperaba, todos los caracteres por debajo de 32, son caracteres de control, no letras. así de sencillo.

Caral 01-02-2007 00:09:23

Muchas Gracias por tu explicacion ArdiIIa
Me queda claro.
Saludos

ArdiIIa 01-02-2007 01:38:11

Por abundar un poquito mas y examinando el ASM de seoane aún se puede optimizar algo mas quitando unas líneas y cambiando otras, seguramente ganemos en rapidez...

Código Delphi [-]
function Longitud(Str: PChar): Integer;
asm
        MOV     Result,0
        DEC     EAX
@@SIG:
        INC     EAX
        CMP     BYTE PTR [EAX],0
        JE      @@SALIR
        CMP     BYTE PTR [EAX],33
        JB      @@SIG
        INC     Result
        JMP     @@SIG
@@SALIR:
end;

roman 01-02-2007 02:53:39

A ver, si lo que se quiere es descontar los saltos de línea y sabemos que hay dos caracteres por cada salto, ¿no es más facil tomar la longitud del texto y restarle 2 veces el número de líneas?

Código Delphi [-]
procedure TForm1.Memo1Change(Sender: TObject);
var
  L, I: Integer;

begin
  L := Memo1.Perform(EM_GETLINECOUNT, 0, 0);
  I := Length(Memo1.Lines.Text) - 2*(L - 1);

  Edit1.Text := Format('%d caracteres', [i]);
end;

// Saludos

seoane 01-02-2007 03:39:31

¿y por que no?

Código Delphi [-]
var
  i,j: integer;
begin
  j:= 0;
  for i:= 0 to Memo1.Lines.Count do
    inc(j,Length(Memo1.Lines[i]));
  Caption:= IntToStr(j);
end;

En los códigos anteriores estamos usando la propiedad Text, que no existe como tal, si no que se crea juntando todas las lineas en un solo string, con la consecuente perdida de tiempo en ese bucle. Así que es mejor acceder directamente a cada linea y sumar sus longitudes, así también eliminamos los saltos de linea y evitamos andar moviendo los caracteres de un string a otro antes de contarlos.

:D cuantas formas de "despellejar a un gato" ...

roman 01-02-2007 03:54:53

La propiedad Text se obtiene mandando el mensaje WM_GETTEXT. La verdad es que hubiera jurado que era como tú decías pero revisando el código de la VCL se ve eso.

// Saludos

seoane 01-02-2007 04:08:01

Cita:

Empezado por roman
La propiedad Text se obtiene mandando el mensaje WM_GETTEXT. La verdad es que hubiera jurado que era como tú decías pero revisando el código de la VCL se ve eso.

// Saludos

Al menos en mi version de delphi no:
Código Delphi [-]
function TStrings.GetTextStr: string;
var
  I, L, Size, Count: Integer;
  P: PChar;
  S, LB: string;
begin
  Count := GetCount;
  Size := 0;
  LB := LineBreak;
  for I := 0 to Count - 1 do Inc(Size, Length(Get(I)) + Length(LB));
  SetString(Result, nil, Size);
  P := Pointer(Result);
  for I := 0 to Count - 1 do
  begin
    S := Get(I);
    L := Length(S);
    if L <> 0 then
    begin
      System.Move(Pointer(S)^, P^, L);
      Inc(P, L);
    end;
    L := Length(LB);
    if L <> 0 then
    begin
      System.Move(Pointer(LB)^, P^, L);
      Inc(P, L);
    end;
  end;
end;

Ahora bien, puede que tu estés hablando de Memo1.Text y no de Memo1.Lines.Text. En el primer caso si que se obtiene con WM_GETTEXT, no así en el segundo caso, como se puede ver en el código anterior.

egostar 01-02-2007 04:10:40

Gracias Roman

Tu código también esta de 10.

YA lo probe y esta perfecto, bien dices seoane, hay muchas fformas de "despellejar a un gato":D

Cuantas formas diferente de hacer esto.

Saludos

roman 01-02-2007 04:18:49

En Delphi 7 es igual, sólo que tú estás hablando de TStrings.Text, pero la propiedad Lines del TMemo es de tipo TMemoStrings que redefine GetTextStr

Código Delphi [-]
function TMemoStrings.GetTextStr: string;
begin
  Result := Memo.Text;
end;

y es Memo.Text la que se obtiene usando WM_GETTEXT.

// Saludos

seoane 01-02-2007 04:23:51

Cita:

Empezado por roman
pero la propiedad Lines del TMemo es de tipo TMemoStrings

:confused:

Código Delphi [-]
  property Lines: TStrings read FLines write SetLines;

roman 01-02-2007 04:29:33

Claro, así está declarada para que sea compatible con TStrings, pero se construye usando TMemoStrings:

Código Delphi [-]
constructor TCustomMemo.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Width := 185;
  Height := 89;
  AutoSize := False;
  FWordWrap := True;
  FWantReturns := True;
  FLines := TMemoStrings.Create;
  TMemoStrings(FLines).Memo := Self;
  ParentBackground := False;
end;

// Saludos

seoane 01-02-2007 04:33:51

Perfecto, esto me aclara algunas cosas. Gracias roman :)

roman 01-02-2007 04:39:43

De hecho, por eso hubiera jurado que era como tú decías. Siendo TStrings una clase independiente de cualquier control, pues no hay forma de obtener el texto total como no sea concatenando de una u otra forma cada línea. Pero esa es la maravilla de TStrings, una clase base sumamente versátil pero perfectamente adecuada para muchos descendientes. Creo que muchos controles hacen los mismo: combos, listbox, etc.

Ahora, si- como sería más lógico -se decide descartar también los espacios en el conteo como comenta Caral, no cabe duda que tú código y el de ArdiIIa en ensamblador son lo óptimo.

// Saludos

egostar 01-02-2007 05:12:06

Cita:

Empezado por roman
Ahora, si- como sería más lógico -se decide descartar también los espacios en el conteo como comenta Caral, no cabe duda que tú código y el de ArdiIIa en ensamblador son lo óptimo.

Pues muchas gracias por los aportes, incluso esto que dices Roman me parece que debo platicarlo con mi cliente porque si, efectivamente, suena lo mas lógico, ademas de hacer lo que nos piden una parte fundamental de ser programador es la de asesorar a los clientes y entregar un producto que se agradezca;) .

Saludos.


La franja horaria es GMT +2. Ahora son las 09:18:45.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi