Sto scrivendo un parser SAX in Java per analizzare un file XML 2,5 GB di articoli di Wikipedia. C'è un modo per monitorare i progressi dell'analisi in Java?Java SAX progresso parser monitoraggio
risposta
Utilizzare un javax.swing.ProgressMonitorInputStream.
Supponendo di sapere quanti articoli hai, non puoi semplicemente tenere un contatore nel gestore? Per esempio.
public void startElement (String uri, String localName,
String qName, Attributes attributes)
throws SAXException {
if(qName.equals("article")){
counter++
}
...
}
(non so se si parsing "articolo", è solo un esempio)
Se non si conosce il numero di articolo in anticipo, sarà necessario contare prima . Quindi è possibile stampare lo stato nb tags read/total nb of tags
, dicono ogni 100 etichette (counter % 100 == 0
).
o addirittura avere un altro thread monitorare lo stato di avanzamento. In questo caso, potresti voler sincronizzare l'accesso al contatore, ma non necessario dato che non ha bisogno di essere veramente accurato.
I miei 2 centesimi
L'ho capito, ma stavo cercando un modo per farlo senza dover prima contare gli articoli. Ho pensato che forse c'era un modo per capire la posizione del parser nel file, perché posso facilmente ottenere la dimensione del file. – Danijel
È possibile ottenere una stima della linea/colonna corrente nel file sovrascrivendo il metodo setDocumentLocator
di org.xml.sax.helpers.DefaultHandler/BaseHandler
. Questo metodo viene chiamato con un oggetto dal quale è possibile ottenere un'approssimazione della linea/colonna corrente quando necessario.
Edit: Per quanto a mia conoscenza, non esiste un modo standard per ottenere la posizione assoluta. Tuttavia, sono sicuro che alcune implementazioni SAX offrono questo tipo di informazioni.
Chiudi, ma poi dovrei conoscere il numero di righe nel file, giusto? – Danijel
Infatti. Un'altra idea potrebbe essere stata sottolineata dall'enigmatico EJP. È possibile stimare il progresso, utilizzando l'avanzamento nel flusso di input. Tuttavia, questo non è il progresso nel parsing, a causa del potenziale buffering e lookaheads. –
userei la posizione di flusso di input. Crea la tua classe di stream banale che delega/eredita da quella "reale" e tiene traccia dei byte letti. Come dici tu, ottenere il file totale è facile. Non mi preoccuperei del buffering, lookahead, ecc. - per file di grandi dimensioni come questi è il chickenfeed. D'altra parte, limiterei la posizione a "99%".
Grazie al suggerimento EJP ProgressMonitorInputStream
, alla fine ho esteso FilterInputStream
in modo che ChangeListener
può essere utilizzato per monitorare la posizione di lettura corrente in termini di byte.
Con questo si ha il controllo più fine, ad esempio, per mostrare più barre di avanzamento per la lettura in parallelo di grandi file xml. Che è esattamente quello che ho fatto.
Quindi, una versione semplificata del flusso monitorabile:
/**
* A class that monitors the read progress of an input stream.
*
* @author Hermia Yeung "Sheepy"
* @since 2012-04-05 18:42
*/
public class MonitoredInputStream extends FilterInputStream {
private volatile long mark = 0;
private volatile long lastTriggeredLocation = 0;
private volatile long location = 0;
private final int threshold;
private final List<ChangeListener> listeners = new ArrayList<>(4);
/**
* Creates a MonitoredInputStream over an underlying input stream.
* @param in Underlying input stream, should be non-null because of no public setter
* @param threshold Min. position change (in byte) to trigger change event.
*/
public MonitoredInputStream(InputStream in, int threshold) {
super(in);
this.threshold = threshold;
}
/**
* Creates a MonitoredInputStream over an underlying input stream.
* Default threshold is 16KB, small threshold may impact performance impact on larger streams.
* @param in Underlying input stream, should be non-null because of no public setter
*/
public MonitoredInputStream(InputStream in) {
super(in);
this.threshold = 1024*16;
}
public void addChangeListener(ChangeListener l) { if (!listeners.contains(l)) listeners.add(l); }
public void removeChangeListener(ChangeListener l) { listeners.remove(l); }
public long getProgress() { return location; }
protected void triggerChanged(final long location) {
if (threshold > 0 && Math.abs(location-lastTriggeredLocation) < threshold) return;
lastTriggeredLocation = location;
if (listeners.size() <= 0) return;
try {
final ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener l : listeners) l.stateChanged(evt);
} catch (ConcurrentModificationException e) {
triggerChanged(location); // List changed? Let's re-try.
}
}
@Override public int read() throws IOException {
final int i = super.read();
if (i != -1) triggerChanged(location++);
return i;
}
@Override public int read(byte[] b, int off, int len) throws IOException {
final int i = super.read(b, off, len);
if (i > 0) triggerChanged(location += i);
return i;
}
@Override public long skip(long n) throws IOException {
final long i = super.skip(n);
if (i > 0) triggerChanged(location += i);
return i;
}
@Override public void mark(int readlimit) {
super.mark(readlimit);
mark = location;
}
@Override public void reset() throws IOException {
super.reset();
if (location != mark) triggerChanged(location = mark);
}
}
non sa - o cura - quanto grande sia il flusso di fondo è, quindi è necessario ottenere qualche altro modo, ad esempio da il file stesso.
Quindi, ecco qui l'utilizzo di esempio semplificato:
try (
MonitoredInputStream mis = new MonitoredInputStream(new FileInputStream(file), 65536*4)
) {
// Setup max progress and listener to monitor read progress
progressBar.setMaxProgress((int) file.length()); // Swing thread or before display please
mis.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) {
SwingUtilities.invokeLater(new Runnable() { @Override public void run() {
progressBar.setProgress((int) mis.getProgress()); // Promise me you WILL use MVC instead of this anonymous class mess!
}});
}});
// Start parsing. Listener would call Swing event thread to do the update.
SAXParserFactory.newInstance().newSAXParser().parse(mis, this);
} catch (IOException | ParserConfigurationException | SAXException e) {
e.printStackTrace();
} finally {
progressBar.setVisible(false); // Again please call this in swing event thread
}
Nel mio caso i progressi sollevano ben da sinistra a destra, senza salti anormali. Regola la soglia per un equilibrio ottimale tra prestazioni e reattività. Troppo piccola e la velocità di lettura può più che raddoppiare su dispositivi piccoli, troppo grandi e il progresso non sarebbe agevole.
Spero che aiuti. Sentiti libero di modificare se riscontri errori o errori di battitura o vota per inviarmi degli incoraggiamenti!: D
Eccellente! Esattamente quello che stavo cercando, lo adatterò, grazie! :) – Matthieu
Penso che questo sarà abbastanza vicino. Grazie! – Danijel
Qualche risposta potrebbe essere più semplice di così ?! :) – Matthieu