Newsgroup: it.comp.java
Titolo: Motore di ricerca

Ho un problema con una tabella alla quale voglio aggiungere un motore di
ricerca: il problema, forse, non e' poi cosi' complicato ma non e' facile
spiegarlo. Io ce la mettero' tutta e, se non capirete qualcosa, chiedete
pure.
Come gia' detto ho una tabella, questa e' formata da 7 colonne a righe
variabili. Vorrei aggiungere a questa un motore di ricerca che date una o
piu' parole, inserite in un JTextField, restituisca tutte le righe che ne
contengono almeno una. La tabella e' costruita a partire da un Vector -rows-
del quale ogni elemento e' una riga e le sette colonne sono divise dal
carattere "|" (per costruire la tabella, naturalmente, viene creato un array
stringa bidimensionale -data[][]-), quindi ho questo vector sul quale ho
pensato di applicare la ricerca (credo sia il modo migliore ma accetto
smentite!). Allego la parte di sorgente che ho *partorito*, ma che non mi
funziona.
<SOURCE>
 void search(String key) {
      data = new String[numLines][7];
      StringTokenizer st = new StringTokenizer(key);
      for (int row=0;row<numLines;row++) {
           // divide nei token di colonna la prima riga
           StringTokenizer st1 = new
StringTokenizer(rows.elementAt(row).toString(),"|",false);
           for (int i=0;i<7;i++) {
                // divide la stringa di ogni colonna in eventuali token
                StringTokenizer st2 = new StringTokenizer(st1.nextToken());
                    while (st2.hasMoreTokens()) {
                         // analizza ogni token della stringa di colonna
confrontandolo
                         // con ogni token della chiave di ricerca
                         while (st.hasMoreTokens()) {
                          if (st2.nextToken().equals(st.nextToken())) {
                               // se il token e' uguale alla chiave di
ricerca la riga viene
                               // aggiunta all'array bidimensionale
                               StringTokenizer st3 = new
StringTokenizer(rows.elementAt(row).toString(),"|",false);
                               for (int col=0;col<7;col++) {
                                    data[row][col] = st3.nextToken();
                               }//end third cicle for
                          }//end second if
                     }//end second cicle while
                }//end first cicle while
           }//end second cicle for
      }//end first cicle for
      System.gc();
 }
</SOURCE>
o  key e' la stringa di ricerca passata al metodo
o  data[][], come gia' detto e' l'array bidimensionale che verra' usato per
creare la tabella
o  numLines e' un int creato precedentemente che identifica il numero di
elementi del vector rows
o  rows e' il vector con le linee della tabella
Mi sembra che non ci sia altro...
Il problema e' che la tabella risultante dall'array data e' vuota.
NB: non da errori in compilazione, non ci sono infatti errori di pura
sintassi.
Come dicevano quei disgraziati dei backstreet boys: tell me why!
Non so piu' dove sbattere la testa (sto pensando ad un duro blocco di pietra
arenaria, salvatemi!)
Grazie in anticipo a chiunque venga la voglia di spremersi le meningi su
quell'intricato metodo,
Nihil
---
nihil est quod ratio facere non posse

 </SOURCE>
Mi sono accorto che, per colpa di quei due -while-, se le stringhe sono
composte da un diverso numero do token (tra di loro), il controllo non viene
effettuato correttamente. Come posso fare? Anche la stringa di ricerca
(-key-) puo' essere composta da piu' di un token!!!
Aiuto, vi prego!!!


 pure.
Premesso che mi devi un'aspirina perché a leggere il tuo codice mi è venuto
il mal di testa ;-), il problema sta nel loop interno. Hai scritto:
while (st2.hasMoreTokens())
{
    // analizza ogni token della stringa di colonna confrontandolo
    // con ogni token della chiave di ricerca
    while (st.hasMoreTokens())
    {
        // ERRATA!
        if (st2.nextToken().equals(st.nextToken()))
        {
            // se il token e' uguale alla chiave di ricerca la riga viene
            // aggiunta all'array bidimensionale
            ...
         }//end second if
     }//end second cicle while
}//end first cicle while
Guarda l'istruzione di comparazione dei token. In un solo colpo passi al
token successivo sia di st2 che di st, con il risultato che i token di st
vengono tutti consumati nel loop interno di scansione dei token di st2. In
questo modo la comparazione ha successo solo se, per pura fortuna, la cella
che stai considerando contiene l'i-esimo token della chiave esattamente in
posizione i-esima. Non solo, dopo che hai controllato la prima cella della
prima riga, st ha già esaurito i suoi token e buonanotte.
Ti riporto la versione corretta del loop interno:
while (st2.hasMoreTokens())
 {
   // AGGIUNTA!
   String st2Token = st2.nextToken();
   // analizza ogni token della stringa di colonna confrontandolo
   // con ogni token della chiave di ricerca
   while (st.hasMoreTokens())
   {
        // AGGIUNTA!
        String stToken = st.nextToken();
        // MODIFICATA!
         if (st2Token.equals(stToken))
         {
             // se il token e' uguale alla chiave di ricerca la riga viene
             // aggiunta all'array bidimensionale
             ...
          }//end second if
   }//end second cicle while
   // AGGIUNTA!
   st = new StringTokenizer(key);
}//end first cicle while
Qualche consiglio:
- scindi la ricerca in sotto-operazioni utilizzando metodi di aiuto (tipo un
metodo che controlla se la tal cella contiene un token della chiave.) Se una
cella contiene un token, puoi già copiare la riga corrispondente nell'array
di uscita ed evitare di confrontare le celle successive della stessa riga
(guadagni in velocità)
- evita di mischiare loop for e cicli while su Enumerator (vedi sopra), è
facile creare problemi
- la parte di codice che copia i valori nell'array data utilizza row come
primo indice. In questo modo l'array di uscita contiene delle righe vuote in
corrispondenza delle righe della tabella originale che non "matchano" la
chiave. Le righe vuote devono poi essere eliminate o saltate. Usa un
contatore di righe individuate usandolo come primo indice di data ed
incrementandolo ad ogni riga che "matcha".
- quando hai a che fare con routine complicate come questa, dissemina il
codice di System.out.println() per tracciarne il funzionamento (io ho fatto
così per capire il codice e trovare il problema che a prima vista mi era
sfuggito...)
Ricordati di mandarmi l'aspirina! 8-)
Marco.
--
Per scrivermi direttamente elimina
__TOGLIMI__ dall'indirizzo e-mail.

 Ricordati di mandarmi l'aspirina! 8-)
Ok, l'indirizzo? :-)


 solo le righe che hanno una parolo uguale a una della chiave! o no?
Cavolo e' vero! Adesso ho capito (arrivo sempre cinque minuti dopo...)
Ok, risolto!
Grazie duemilauno per i tuoi consigli, ma adesso non so come fare nel caso
la stringa di ricerca sia composta da piu' di una parola, hai qualche
consiglio?
Nihil

Woa, ce l'ho fatta... (la notte mi ha portato consiglio!)
posto la soluzione, per altro temporanea, che ho scritto stamattina.
Adesso lavorero' ad una suddivisione in diversi void per rendere il tutto
piu' leggibile (potrei aver bisogno di modificarla in futuro!), ma intanto
funziona, bene, ed e' velocissimo! L'ho provato su di una tabella con piu'
di cento linee facendo una ricerca a tre parole: ci ha messo meno di un
secondo! Ci ha messo piu' tempo ha caricare la tabella risultante!!!
I commenti sono ben'accetti.
<SOURCE>
 public void search(String key) {
  // creo un nuovo array temporaneo per il
  // trasferimento delle righe che mi interessano
  String[][] newData = new String[numLines][7];
  // uso questo intero per contare il numero di linee
  // che matchano la ricerca
  int temp = 0;
  for (int row=0;row<numLines;row++) {
   for (int i=0;i<7;i++){
    // divide in token le stringhe di ogni cella
    StringTokenizer st1 = new StringTokenizer(data[row][i]);
    while (st1.hasMoreTokens()) {
     // finche' la cella ha nuovi token *trasforma* in stringa
     // il token della cella
     String tok1 = st1.nextToken();
     // divide in token la stringa di ricerca
     StringTokenizer st2 = new StringTokenizer(key);
     while (st2.hasMoreTokens()) {
      // finche' la stringa di ricerca ha nuovi token
      // li *trasforma* in stringhe
      String tok2 = st2.nextToken();
      if (tok1.equals(tok2)) {
       // se le due stringe sono uguali le aggiunge
       // all'array temporaneo
       for (int col=0;col<7;col++) {
        newData[temp][col] = data[row][col];
       }
       // il numero di righe viene incrementato
       temp++;
      }// fine if
     }// fine secondo ciclo while
    }// fine primo ciclo while
   }// fine secondo loop for
  }// fine primo loop for
  // all'intero numLines viene attribuito il valore del
  // contatore temporaneo
  numLines = temp;
  createSearchTable(newData);
  System.gc();
 }
 // aggiorna il conenuto dell'array data attribuedogli
 // quello dell'array newTable meno le righe in eccesso
 void createSearchTable(String[][] newRows) {
  data = new String[numLines][7];
  for (int row=0;row<numLines;row++) {
   for (int col=0;col<7;col++) {
    data[row][col] = newRows[row][col];
   }
  }
 }
</SOURCE>
Nihil
---
nihil est quod rati facere non posse