Esercizio:

Scrivere un programma per leggere un file testo scelto dall'utente e costruire una lista in ordine alfabetico di tutte le parole presenti nel file. Tutte le parole vanno convertite in minuscolo ed i duplicati rimossi.
Non è ovvio come separare un file in parole mentre viene letto. Possiamo usare il metodo seguente:

/**
* Ignorare tutto quello che si trova prima della prima lettera. Se si trova un
* end-of-file, return null. Altrimenti, leggi e restituisci una parola, intesa
* come sequenza di lettere. Una parola 'potrebbe' contenere un apostrofo,
* se preceduto e seguito da lettere.
* @return la parola successiva da TextIO, o null se si arriva a end-of-file
*/
private static String readNextWord() {
char ch = TextIO.peek(); // Guarda il carattere successivo in input.
while (ch != TextIO.EOF && ! Character.isLetter(ch)) {
TextIO.getAnyChar(); // Leggi il carattere.
ch = TextIO.peek(); // Guarda il prossimo carattere.
}
if (ch == TextIO.EOF) // end-of-file
return null;
// A questo punto, il carattere successivo è una lettera
String word = ""; // per contenere la parola letta.
while (true) {
word += TextIO.getAnyChar(); // Aggiungi la lettera alla parola.
ch = TextIO.peek(); // Guarda il prossimo carattere.
if ( ch == '\'' ) {
// Se il carattere succcessivo è un apostrofo, verifica se
// è seguito da una lettera, nel qual caso aggiungi ambedue
// e continua. Se quello che segue l'apostrofo non è una lettera,
// la parola è finita e si esce dal loop.
TextIO.getAnyChar(); // Leggi l'apostrofo.
ch = TextIO.peek(); // Guarda il carattere successivo.
if (Character.isLetter(ch)) {
word += "\'" + TextIO.getAnyChar();
ch = TextIO.peek(); // Guarda il carattere successivo.
}
else
break;
}
if ( ! Character.isLetter(ch) ) {
// Se il carattere successivo non è una lettera,
// la parola è finita, e si esce dal loop.
break;
}
// Se non siamo usciti dal loop, il carattere successivo è una lettera.
}
return word;
}

Questo metodo restituisce null quando la lettura è completata.


Ci sono vari approcci a questo problema. Uno è di scaricare tutte le parole dal file ad una lista, senza preoccuparsi di rimuovere duplicati o di mantenerle in ordine. Dopo la lettura, viene ordinato e stampato, rimuovendo i duplicati durante la stampa.

for (int i = 0; i < wordlist.size(); i++) {
if (i == 0 || ! wordlist.get(i).equals(wordList.get(i-1) )
TextIO.putln(wordlist.get(i));
}

Un secondo approccio è di costruire la lista direttamente ordinata con un metodo insert() (simile al metodo InsertionSort).  

Un terzo approccio, dettagliato di seguito, elimina i duplicati mentre il file viene letto.  Alla fine della lettura, non rimane che ordinare il file usando, per esempio, un SelectionSort. L' output viene prodotto usando un  for-each loop:

for (String w : wordList)
TextIO.putln(" " + w);

I methodi TextIO.readUserSelectedFile() e TextIO.writeUserSelectedFile() mostrano un file dialog per selezionare i file in lettura e in scrittura.    Senza un file di output, la lista di parole viene prodotta su standard output (schermo).  

Errori durante l'utilizzo di TextIO generano una eccezione di tipo IllegalArgumentException


import java.util.ArrayList;


public class ListAllWordsFromFile {


public static void main(String[] args) {

System.out.println("\n\nThis program will ask you to select an input file.");
System.out.println("After reading the file, select an output file for the list");
System.out.println("of words in order, without repetitions and in lower case.\n\n");
System.out.print("Press return to begin.");
TextIO.getln(); // Aspetta che l'utente prema return.

try {
if (TextIO.readUserSelectedFile() == false) {
System.out.println("No input file selected. Exiting.");
System.exit(1);
}
ArrayList<String> wordList = new ArrayList<String>();
String word = readNextWord();
while (word != null) {
word = word.toLowerCase(); // convertire in lower case
if ( wordList.indexOf(word) == -1 ) {
// parola nuova da aggiungere alla lista
wordList.add(word);
}
word = readNextWord();
}
System.out.println("Number of different words found in file: "
+ wordList.size());
System.out.println();
if (wordList.size() == 0) {
System.out.println("No words found in file.");
System.out.println("Exiting without saving data.");
System.exit(0);
}
selectionSort(wordList);
TextIO.writeUserSelectedFile(); // Se utente seleziona cancel, automaticamente
// a standard output.
TextIO.putln(wordList.size() + " words found in file:\n");
for (String w : wordList)
TextIO.putln(" " + w);
System.out.println("\n\nDone.\n\n");
}
catch (Exception e) {
System.out.println("Sorry, an error has occurred.");
System.out.println("Error Message: " + e.getMessage());
}
System.exit(0); // può essere necessario se si usano i file dialogs.
}


/**
* Ordina la lista di parole lessicograficamente
*/
private static void selectionSort(ArrayList<String> list) {
for (int top = list.size() - 1; top > 0; top--) {
int indexOfBiggest = 0;
for (int j = 0; j < top; j++) {
String str = list.get(j);
if (str.compareTo( list.get(indexOfBiggest) ) > 0) {
indexOfBiggest = j;
}
}
String temp = list.get(top);
list.set( top, list.get(indexOfBiggest) );
list.set( indexOfBiggest, temp );
}
}