Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: AlexLazarus - Novembre 10, 2022, 06:53:44 pm

Titolo: [RISOLTO] Gestione vocali accentate, spazio e caratteri alfabetici
Inserito da: AlexLazarus - Novembre 10, 2022, 06:53:44 pm
Risolto un problema, se ne presenta ovviamente(!) un altro.
Come già detto, nel topic sulle vocali accentate (https://www.lazaruspascal.it/index.php?topic=2680.0) tutto funziona perfettamente ma...
Ma inserendo la routine all'interno di un programma più complesso sorgono errori incomprensibili. Dunque...

Lo scopo è, in poche parole, estrarre da un file di testo TXT tutte le parole formate da caratteri alfabetici (con eventuali vocali accentate) escludendo tutti gli altri, eventualmente convertendole tutte in minuscolo / maiuscolo.

Ho iniziato con vari tentativi, partendo proprio dal carattere di spazio.

1) Il file banale di testo testo Varie_parole.TXT (1 nello screenshot) sono presenti diversi righi, alcuni dei quali contengono una sola parola senza vocali accentate, altri parole con vocali accentate, altri ancora più parole separate da uno spazio (lasciate perdere il significato, è solo un esempio).

2) Caricando il file di testo (con il programma che riporto in calce) nel ListBox1 (2) lo sottopongo a elborazione premendo il Button1 ("Separa parole", 3), infine (ma non è necessario) elimina le eventuali righe vuote (4a e 4b).

3) Nel listbox2 dovrebbero(!) essere trasferite le singole parole (4a e 4b), eventualmente suddivise su più righi se in listbox1 sono su un unico rigo, separate da spazio.
 
Come si può notare alcune parole vengono correttamente suddivise mentre altre no. Inoltre alcune presentano spazi all'inizio, in altre viene escluo l'ultimo carattere.



E' da stamattina che sto facendo vari tentativi ma non riesco proprio a capire dove sbaglio.

Codice: [Seleziona]
unit A_2022_11_09_Prog_230_Pulisci_file_Testo_A_pas;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Menus, StdCtrls, LazUtf8;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    ListBox1: TListBox;
    ListBox2: TListBox;
    MainMenu1: TMainMenu;
    MenuItem1: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem5: TMenuItem;
    MenuItem6: TMenuItem;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ListBox2Click(Sender: TObject);
    procedure MenuItem2Click(Sender: TObject);
    procedure MenuItem5Click(Sender: TObject);
    procedure MenuItem6Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.MenuItem2Click(Sender: TObject);
// Carica file di testo in ListBox1
Var NomeFile: string;
Var sl: TStringList;
  begin
    if OpenDialog1.Execute then
    Begin
    NomeFile:= OpenDialog1.FileName;
    //ShowMessage (NomeFile);
      if FileExists(NomeFile) then
          Begin
            ShowMessage('Il file: ' + NomeFile + ' esiste');
            end
            else if  FileExists(NomeFile) =False Then
            ShowMessage('Il file: ' + NomeFile + ' NON esiste');

       sl:=TStringList.Create;
       try
          sl.LoadFromFile(NomeFile);
          ShowMessage('righe lette: ' + IntToStr(sl.Count));
          ListBox1.Items.LoadFromFile(NomeFile);
          finally
          sl.Free;
       end;
    end;
  end;

procedure TForm1.MenuItem5Click(Sender: TObject);
begin
 Label1.Caption:='';
 Label2.Caption:='';
 Label2.Caption:='';
 ListBox1.Clear;
end;

procedure TForm1.MenuItem6Click(Sender: TObject);
begin
 Label1.Caption:='';
 Label2.Caption:='';
 Label2.Caption:='';
 ListBox2.Clear;
end;

procedure TForm1.Button1Click(Sender: TObject);
// Separa parole e le mette in ListBox2
//Var Rigo,Car1, Car2, Parola, tempstr:String;
Var Car1, Car2, Rigo, Parola, tempstr :String;
Var  UTtempstr: UnicodeString;
Var I,Y, Numero_Massimo_Righe:Integer;
begin
Parola:='';
Numero_Massimo_Righe:=ListBox1.items.count;
Label1.Caption:=('');
Label2.Caption:=('');
For I := 0 to Numero_Massimo_Righe-1 do
     Begin
          Parola:= '';
          Rigo:= ListBox1.items.strings[I];
          tempstr := Rigo;
          UTtempstr := tempstr;
        for Y := 0 to UTF8Length(tempstr)-1 do
            Begin
             Car1 :=Copy(rigo,Y+1,1);
             car2 := Utf8Copy(tempstr, Y+1, 1);
                  // If ( (Car2 = 'è') Or (Car2 = 'è') Or (Car2 = 'é') Or (Car2 = 'ò' )Or (Car2 = 'à') Or (Car2 = 'ù') Or (Car2 = 'ì') Or (Car2 = ' ') Or (Car2 = '-') Or (Car2 = '(') Or (Car2 = ')') Or (Car2 = ',')) Then
                 If ( (Car2 = 'è') Or (Car2 = 'é') Or (Car2 = 'ò' )Or (Car2 = 'à') Or (Car2 = 'ù') Or (Car2 = 'ì') Or (Car2 = ' ')) Then
                     Begin
                          Parola:=Parola + Car2;
                          If Car1 =' '  then
                            Begin
                                 ListBox2.items.add(Parola);
                                 Parola:= '';
                            end;
                     End;
                 //Else
                 Parola:= Parola + LowerCase(Car1);
                      If Utf8Copy(tempstr, Y+1, 1) = (' ') then
                 //If Car1 = (' ')then    //  If Car1 = Chr(32)
                    Begin
                         ListBox2.items.add(Parola);
                         Parola:='';
                  end;
           end;
        ListBox2.items.add(Parola);
        Parola:='';
        end;
 Label2.Caption:='N. parole: ' + IntToStr(ListBox2.items.count);
end;

procedure TForm1.Button2Click(Sender: TObject);
// Conta i caratteri dell'intero file caricato
Var Rigo: String;
Var N_Caratteri: QWord;
Var I, Y, Z: Integer;
begin
N_Caratteri:=0;
Label1.Caption:='';
Label2.Caption:='';

     I := ListBox1.items.count;
 For Y := 0 to I-1 do
      Begin
       Rigo:= ListBox1.items.strings[Y];
       Z:= Length(Rigo);
       N_Caratteri:= N_Caratteri + Z;
      end;
 Label4.Caption:=('N. caratteri: ' + IntToStr(N_Caratteri));
end;

procedure TForm1.Button3Click(Sender: TObject);
// Elimina parole di lunghezza nulla
Var N_Parole: QWord;
Var I, Y,Z: Integer;
Var  Rigo: string;
begin
 N_Parole:= 0;
  I := ListBox2.items.count;
 For Y := I-1 downto 0 do
      Begin
           ListBox2.itemindex := Y;
           Rigo:= ListBox2.items.strings[Y];
           Z:= Length(Rigo);
       If (Z = 0) OR (Rigo =(' ')) then
           Begin
            ListBox2.DeleteSelected;
           end;
      end;
 Label1.Caption:=('Spazi vuoti eliminati: ' + IntToStr( ListBox2.items.count));
 end;

procedure TForm1.FormCreate(Sender: TObject);
begin

end;

procedure TForm1.ListBox2Click(Sender: TObject);
begin
  //Label1.Caption:= ListBox2.items.strings[ListBox2.itemindex];
  Label3.Caption:='Lunghezza parola selezionata: ' + IntToStr(Length(ListBox2.items.strings[ListBox2.itemindex]));
end;


end.

...ed ecco il banale file di testo:

Codice: [Seleziona]
Perchà
Pà ab
c
d
v b
Perchò
Perchù
Perchì
Perché mai dovrei quantunquemente
Tre parolone basta

Quattro fdkgjh
Cinque lhgfb
g
[/]
Titolo: Re:Gestione vocali accentate, spazio (e altri caratteri esclusivamente alfabetici)
Inserito da: DragoRosso - Novembre 10, 2022, 07:29:03 pm
Non puoi usare gli strumenti standard tipo:
Codice: [Seleziona]
 Car1 :=Copy(rigo,Y+1,1);
con i caratteri unicode. Devi usare o le stringhe Unicode (UnicodeString) o le funzioni Utf8Copy ad esempio.

Questo per tutto ciò che non è una semplice concatenazione (string + string).

Il Length, il Copy, etc ..... sono metodi che non funzionano con le stringhe che contengono caratteri UNICODE !!!

Secondo me forse è meglio che definisci le stringhe come UnicodeString, e dovresti avere risolto. Ovviamente su quelle stringhe non puoi usare Utf8Copy o procedure simili ma userai le normali funzioni Copy, Length, etc ...

P.S.: anche la funzione "lowercase" potrebbe non funzionare con i caratteri Unicode. Non tutti i caratteri hanno un "lowercase" o un "uppercase". Non sò come si comporta la funzione quando trova questi caratteri.

P.S.2: in ogni caso, se vuoi suddividere per parola ti consiglio di studiare la funzione "SplitString", con quella fai più o meno quello che vorresti fare ma che non stai facendo .... evviva i giochi di parole  ;D

Ciao
Titolo: Re:Gestione vocali accentate, spazio (e altri caratteri esclusivamente alfabetici)
Inserito da: bonmario - Novembre 11, 2022, 08:16:44 am
Ciao,
visto che leggi da un file di testo, potresti "convertire al volo" quello che leggi.

Io mi sono fatto anni fa questa funzioncina, che uso in queste occasioni per semplificarmi la vita !!!
Codice: [Seleziona]

type TTipoOperConvStr=(tocLeggiDaFileTxt, tocScriviFileTxt);

  function ConvertiStringaFileTxt(St: String; TipoConvStr: TTipoOperConvStr): String;
  begin
    {$IFDEF MSWINDOWS}
      case TipoConvStr of
        tocLeggiDaFileTxt: St:={CP1252ToUTF8(St)}WinCPToUTF8(St);
        tocScriviFileTxt:  St:={UTF8ToCP1252(St)}UTF8ToWinCP(St);
      end;
    {$ENDIF}
    Result:=St;
  end;

Nel caso in cui non ci sia già, devi aggiungere "LazUTF8" alla uses.

Ciao, Mario
Titolo: Re:Gestione vocali accentate, spazio (e altri caratteri esclusivamente alfabetici)
Inserito da: AlexLazarus - Novembre 11, 2022, 09:33:55 am
[...] ti consiglio di studiare la funzione "SplitString",[...]

Buona idea, ma c'è un problema: volendo separare anche altri caratteri (parentesi, virgole, apostrofi eccetera), credo occorra fare una concatenazione di If ... Then auto-ricorsiva (non so se mi sono spiegato bene).
Comunque, ecco una prima bozza, magari da studiarci su:
Codice: [Seleziona]
procedure TForm1.Button1Click(Sender: TObject);
Var Stringa: TStringArray;
Var Spazio, OK: string;
  Var I,X,Y: Integer;
begin
    Y:=0;
  For X:=0 to length(Edit1.text) do
      Begin
        If Copy(Edit1.Text,X,1) = ' ' then Y:= Y +1;
      end;
  OK:= Edit1.text;
        For I := 0 to Y do
          Begin
            Stringa := OK.Split(' ');
            ListBox1.items.add(Stringa[I]);
          end;
end;
Titolo: Re:Gestione vocali accentate, spazio (e altri caratteri esclusivamente alfabetici)
Inserito da: Stilgar - Novembre 11, 2022, 10:21:09 am
Ciao
https://www.freepascal.org/docs-html/rtl/strutils/splitstring.html


Prova ad usare questa funzione. Puoi mettere tutti i caratteri che vuoi, non solo lo spazio.

Stilgar
Titolo: Re:Gestione vocali accentate, spazio (e altri caratteri esclusivamente alfabetici)
Inserito da: AlexLazarus - Novembre 13, 2022, 08:18:47 am
Tutto (o quasi...) funziona a dovere. Qui di seguito il mio piccolo contributo a chi ha seguito questo 3D.
Un ringraziamento speciale a DragoRosso che mi ha voluto dare una dritta. 😉

La procedura:
Codice: [Seleziona]
procedure TForm1.Button5Click(Sender: TObject);
// Pulsante Split 2
Var Stringa: TStringArray;
Var OK, Car_1 : string;
Var IA,XA,YA, ZA: Integer;
begin
 If ListBox1.items.count > 0 then
 Begin
 For ZA := 0 to  ListBox1.items.count -1 do // Esamina tutte le righe di ListBox1
         begin
              For XA:=0 to length(ListBox1.items.strings[ZA]) do // Esamina uno alla volta tutti caratteri della riga di ListBox1
                  Begin
                       Car_1:= Copy(ListBox1.items.strings[ZA],XA,1);
                       If Copy(ListBox1.items.strings[ZA],XA,1) = chr(32) then YA:= YA +1;
                  end;
              // ShowMessage(Copy(ListBox1.items.strings[ZA],XA,1) + '    YA: ' + IntToStr(YA) + '    ZA: ' + IntToStr(ZA));
                  OK:= ListBox1.items.strings[ZA];
                       For IA := 0 to YA +1 do
                           Begin
                                Stringa := OK.Split(' ');
                                ListBox2.items.add(Stringa[IA]);
                           end;
                  YA:=0;
         end;
end
 else ShowMessage('Non ci sono righe di caratteri nel ListBox1!');
end;

Il file .TXT dell'esempio visibile negli screenshot:

Codice: [Seleziona]
Questa è una frase in una riga

Qui sopra c'è un rigo vuoto
Qui di seguito ci sono tre   spazi
Per di più qui c'è un rigo
Grazie all'Italian community of Lazarus and Free Pascal
Titolo: Re:[RISOLTO] Gestione vocali accentate, spazio e caratteri alfabetici
Inserito da: DragoRosso - Novembre 13, 2022, 11:37:19 am
Prova questo:

Codice: [Seleziona]
  //Aggiungi nella USES l'unità "Types":
  Uses: Types, .......
  //
  var p: TStringDynArray;
  //Qui carichi la ListBox, occhio all'encoding !!!!!!!!!!!!!!!!!!!!
  ListBox1.Items.LoadFromFile('c:\temp\prova.txt', TEncoding.UTF8);
  //Qui è quanto serve per fare ciò che vuoi
  p := ListBox1.Items.Text.Split([' ','.',',',':',';','?','!',#1310,#13,#10], TStringSplitOptions(1));
  ListBox2.Items.AddStrings(p, true);


Ciao
Titolo: Re:[RISOLTO] Gestione vocali accentate, spazio e caratteri alfabetici
Inserito da: AlexLazarus - Novembre 13, 2022, 04:20:03 pm
Prova questo:

Codice: [Seleziona]
  //Aggiungi nella USES l'unità "Types":
 
[ ... ] 


Ciao

E adesso me lo dici? 😡

Ovviamente: grazie! 😉 Lo proverò appena posso.
Titolo: Re:[RISOLTO] Gestione vocali accentate, spazio e caratteri alfabetici
Inserito da: AlexLazarus - Novembre 14, 2022, 10:35:57 am
Prova questo:

Codice: [Seleziona]
  //Aggiungi nella USES l'unità "Types":
 [ ... eccetera ... ] 

MERAVIGLIOSO!
Tuttavia...
1) Compare un messaggio inquietante.
2) Non accetta alcuni caratteri speciali:

Codice: [Seleziona]
 p := ListBox1.Items.Text.Split([' ' , '.' , ',' , ':' , ';' , '?' , '!' , '(' , ')' , chr(39) , '"' , chr(174) , chr(175) , chr(203) , chr(194) ,  '*' , '/' , '1' , '2' , '3', '4', '5', '6' , '7' , '8' , '9' , '0' , chr(45),     #1310,#13,#10], TStringSplitOptions(1));

Poco male, ci mancherebbe!  ;)
Titolo: Re:[RISOLTO] Gestione vocali accentate, spazio e caratteri alfabetici
Inserito da: DragoRosso - Novembre 14, 2022, 02:09:06 pm

Tuttavia...
1) Compare un messaggio inquietante.
2) Non accetta alcuni caratteri speciali:


Il messaggio non è così inquietante, ce ne sono di peggiori (a cui magari fa seguito una bella BSOD di Windows  :o  :P )....

I caratteri ANSI che immetti con valore ASCII al disopra del 127 dovrebbero essere interpretati come caratteri unicode. Non sò sinceramente come si possano comportare sia il compilatore che l'Helper Split.

Potrebbe essere utile forzare il supporto a Unicode del compilatore tramite le OPZIONI PROGETTO, aggiungendo la voce indicata nell'immagine (-FcUTF8).

Ciao
Titolo: Re:[RISOLTO] Gestione vocali accentate, spazio e caratteri alfabetici
Inserito da: AlexLazarus - Novembre 15, 2022, 06:38:32 pm
Citazione
Potrebbe essere utile forzare il supporto a Unicode del compilatore tramite le OPZIONI PROGETTO, aggiungendo la voce indicata nell'immagine (-FcUTF8).

Ho aggiunto FcUTF8, ma l'unica variazione è l'assenza di messaggi.
Titolo: Re:[RISOLTO] Gestione vocali accentate, spazio e caratteri alfabetici
Inserito da: SB - Novembre 16, 2022, 03:48:33 pm
Vista la notevole varietà di caratteri che potresti trovare nel testo, secondo me la strada che avevi intrapreso inizialmente non era male. Va solo aggiustata un po'.
Metti in un'unica stringa tutti i caratteri che decidi di accettare come formanti una parola e per esclusione tutti gli altri diventano separatori:
Validi := 'abcdefgh...ABCDE...0123...àòùèì...';
Valuti un carattere alla volta usando Pos(Carattere, Validi) anzichè una sfilza di if
Costruisci le stringhe delle parole e sei a posto

Secondo me lo Split() va bene se i separatori sono pochi e noti

Invece di costruire le stringhe un carattere alla volta, potrebbe essere più conveniente individuare l'indice iniziale e l'indice finale e poi estrarre la sottostringa

Titolo: Re:[RISOLTO] Gestione vocali accentate, spazio e caratteri alfabetici
Inserito da: AlexLazarus - Novembre 17, 2022, 03:59:09 pm
Metti in un'unica stringa tutti i caratteri che decidi di accettare come formanti una parola e per esclusione tutti gli altri diventano separatori:
Validi := 'abcdefgh...ABCDE...0123...àòùèì...';
Valuti un carattere alla volta usando Pos(Carattere, Validi) anzichè una sfilza di if
[... eccetera ...]

Vero: l'algoritmo che ho impostato non è per nulla efficiente, me ne rendo conto. Un po' per volta apporterò modifiche, pubblicando su questo forum gli aggiornamenti.