Newsgroup: it.comp.java
Titolo: Read Big Files

	at FileData.main(FileData.java:10)
 
Quando uso il seguente codice:
 
import java.io.IOException;
import java.io.FileReader;
import java.io.BufferedReader;
 
 
public class ReadFile {
 
	private String path;
	public ReadFile(String file_path) {
		path = file_path;
	}
 
	public String[] openFile() throws IOException {
		FileReader fr = new FileReader(path);
		BufferedReader textReader = new BufferedReader(fr);
 
		int numberOfLines = countLines();
		String[] textData = new String[numberOfLines];
 
		int i;
		for (i=0; i < numberOfLines; i++) {
			textData[i] = textReader.readLine();
		}
 
		textReader.close();
		return textData;
	}
 
	private int countLines() throws IOException {
		FileReader fr = new FileReader(path);
		BufferedReader textReader = new BufferedReader(fr);
 
		String aLine;
		int numberOfLines = 0;
 
		while ((aLine = textReader.readLine()) != null) {
			numberOfLines++;
		}
 
		textReader.close();
		return numberOfLines;
	}
 
	public Double[][] graphData() throws IOException {
		int numberOfLines = countLines();
		String[] textData = openFile();
		Double[][] graphData = new Double[numberOfLines][3];
		int i;
		for (i=0; i < numberOfLines; i++) {
		    String aString = textData[i];
			String[] strArray = aString.split(",");
			Double high = Double.parseDouble(strArray[3]);
			Double low= Double.parseDouble(strArray[4]);
			Double vol = Double.parseDouble(strArray[6]);
			graphData[i][0] = high;
			graphData[i][1] = low;
			graphData[i][2] = vol;
		}
 
		return graphData;
	}
 
	public int write() throws IOException {
		return countLines();
	}
}
 
 
 
Che deve leggere alcuni valori di ogni riga di una sequenza di dati
nel seguente formato di un file di testo che e' mooooolto grande e
metterli in un array
formato del fil testo:
2000.01.03,00:15,1.00780,1.00790,1.00770,1.00780,4
2000.01.03,00:16,1.00780,1.00790,1.00770,1.00780,4
2000.01.03,00:17,1.00790,1.00790,1.00770,1.00770,21
2000.01.03,00:18,1.00780,1.00830,1.00760,1.00780,12
2000.01.03,00:20,1.00790,1.00820,1.00780,1.00800,6
2000.01.03,00:21,1.00810,1.00810,1.00790,1.00790,21
2000.01.03,00:25,1.00820,1.00820,1.00820,1.00820,1
 
Il programma prende 3 di questi valori elencati in ogni riga e li
trasferisce come double in un array che utilizzero sucessivamente per
creare grafici.
 
Il programma funziona ma quando provo a leggere da un file che
contiene qualcosa come un milione di righe (file txt da 250MB o_O )
come quelle elencate sopra.. ricevo l-errore di piu' sopra.
 
Potreste anche solo indirizzarmi su come possa dire a Java di leggere
solo porzioni del tal file? magari partendo da una certa stringa fino
ad un altra stringa?
 
Immagino che debba sempre comuque far prima leggere tutto il file per
trovare le stringhe di riferimento.. e quindi ci sarebbe comquneu l-
errore... ma magari c'e' qualche trucco?
 
 
grazie per qualsiasi aiuto

 ma magari c'e' qualche trucco?
 
Java ti da' OutOfMemoryError semplicemente perche' con tutte le varie
new String() e new Double() superi il limite di memoria che la virtual 
machine ha a disposizione.
 
Il problema non e' tanto leggere il file un po' per volta, in quanto lo 
potresti gia' fare con readLine(), il problema e' che la usi in un ciclo 
for per salvarti tutte le linee e poi per parsificarle tutte assieme.
 
Dovresti continuare a leggere il file readline() senza salvarti tutte le 
righe e parsificarle/plottarle man mano che le leggi, e non alla fine.
 
Cioe' fare:
for (i=0; i < numberOfLines; i++) {
	textData[i] = textReader.readLine();
}
e' ben diverso da fare:
for (i=0; i < numberOfLines; i++) {
	String line = textReader.readLine();
	// ... qui parsifichi e plotti line
}
Nel secondo caso a ogni iterazione sovrascrivi il valore di line
e quindi non mantieni un riferimento alla memoria ritornata da 
readline() e cosi' verra' liberata.
 
Oppure continui a fare come adesso ed aumenti la memoria a disposizione 
della macchina virtuale java con l'opzione di avvio -Xmx512m (per 
esempio a 512 Mb).
 
Ciao.

Grazie, e' gia' un ottima informazione sapere che il problema non e'
nell'aprire il file ma e' nel cercare di metterlo interamente in
memoria.
 
Tuttavia non posso plottare mano a mano come dici perche' mi servira'
soltanto di plottare parte di quei dati.
 
I file enorme e' l'andamento di una valuta nell'arco degli ultimi 10
anni in periodi di un minuto.
L'utente del mio programma selezionera' un periodo dando due date... e
il mio programma dovrebbe aprire quel grosso file e prendere i dati
relativi a quel periodo e plottare gli stessi.
 
Forse dovrei cercare la data che da l-utente all-interno del grande
file con un ciclo for tipo il tuo dove al posto del plot c'e' un
comando di confronto.
quando trovo la data di inizio e fine periodo mi salvo le coordinate e
apro il file solo in quella zona delimitata dalle coordinate.
 
Il problema e' che non ho la minima idea di quale comando mi possa
fornire le coordinate di un file... e soprattutto non so una volta
avute le coordinate come aprire il file attraverso le stesse.
 
Avresti delle dritte per indirizzarmi?
 
La cosa che piu' si avvicina ho trovato essere le classi
BufferedInputStream.read e FileInputStream.read che hanno un parametro
di OFFSET. credo che dando un valore in bytes apriranno il file dopo
tot bytes.
Il problema e' come capire quanti bytes sono passati dopo la ricerca
della data voluta.
bah sono sulla buona strada?
 
Comunque pr questo cercavo di crearmi un array con tutto il file
dentro.. cosi' era piu' semplice trovare le coordinate per un certo
periodo usando i riferimenti dell'array.. ma sono d'accordo che non e'
un metodo snello e smart di procedere

 soltanto di plottare parte di quei dati.
 
Ho esagerato, basterebbe gia' solo che lo parsifichi riga per riga man 
mano che lo leggi senza memorizzare tutte le linee.
 
Ti faccio un esempio scritto molto alla veloce per darti una idea, di 
come risparmiare gia' un po' di memoria. Ciao
 
import java.io.*;
import java.util.*;
 
class MyData {
     private Double high, low, val;
     public MyData(Double high, Double low, Double val) {
         this.high = high;
         this.low = low;
         this.val = val;
     }
     public Double getHigh() {
         return high;
     }
     public Double getLow() {
         return high;
     }
     public Double getVal() {
         return high;
     }
}
 
class Parser {
     public ArrayList<MyData> parse() throws IOException {
         ArrayList<MyData> result = new ArrayList<MyData>();
         FileReader fr = new FileReader("prova.txt");
         BufferedReader textReader = new BufferedReader(fr);
 
         while (true) {	
             String line = textReader.readLine();
             if (line == null)
                 break;
             String[] strArray = line.split(",");
             result.add(new MyData(Double.parseDouble(strArray[3]),
                                   Double.parseDouble(strArray[4]),
                                   Double.parseDouble(strArray[6])));
		}
         return result;
	}
 
     public static void main(String args[]) throws IOException {
         ArrayList<MyData> result = new Parser().parse();
         for (int i = 0; i < result.size(); i++) {
             MyData data = result.get(i);
             System.out.println(data.getHigh() + " " + data.getLow() + " 
" + data.getVal());
         }
     }
}
 

 come risparmiare gia' un po' di memoria. Ciao
 
Riguardo al seek a cui alludevi IMHO (sempre che io abbia capito come 
sono fatti i files) non ne hai bisogno:
- fai readline() fino a quando trovi la data di inizio
- da quel momento parsifichi le line e ti salvi i risultati (vedi come 
appendevo MyData a ArrayList)
- quando trovi la data di fine, interrompi parsificazione e readline
- ritorni quindi come risultato l'ArrayList cosi' popolato
 
Ciao.

Grazie per il tuo codice, ci sono molti spunti...
 
non e' che mi sai suggerire dove trovare informazioni su come usare
l'arrayList nel modo che usi tu?
 
questo mettere la classe tra <> e' al quanto strano e faccio fatica a
capirne il funzionamento.. per di piu' in rete trovo solo esempi
diversi su come usare arraylist
 
Grazie per qualsiasi info!
 
cmq ho risolto il problema cercando prima il punto in cui voglio
 
 
Per la cronaca anche col tuo codice il file era troppo pesante alla
fine ho risolto la lettura dell'area del file che mi interessa con
questo codice: (la parte in cui creo l'array non funziona perche non
so usarlo ancora.. ma il grande file viene letto facilmente ora
perche' comincia a costruire array solo quando c'e' la zona
interessata)
 
	public ArrayList<MyData> sectorLines(String startDate, String
endDate) throws IOException {
 
		BufferedReader textReader = new BufferedReader(new
FileReader(path));
		String aLine;
		int lines = 0;
		boolean x = false;
		ArrayList<MyData> result = new ArrayList<MyData>();
 
		while((aLine = textReader.readLine()) != null){
 
			lines++;
 
            if(startDate.equals(aLine)) {
				x = true;
			}
 
			if(x == true) {
				String[] strArray = aLine.split(",");
				result.add(new MyData( Integer.parseInt(strArray[0]),
									Integer.parseInt(strArray[1]),
 
Integer.parseInt(strArray[2]),
									Integer.parseInt(strArray[3])));
			}
 
			if(endDate.equals(aLine)) {
				break;
			}
		}
		return result;
	}

 l'arrayList nel modo che usi tu?
 
Cercando "Generics" trovi tutte le informazioni. Puoi partire da qui:
http://it.wikipedia.org/wiki/Generics_Java
 
Ciao