Piszę parser SAX w Javie, aby przeanalizować plik XML 2,5GB artykułów wikipedia. Czy istnieje sposób monitorowania postępu parsowania w Javie?Monitorowanie postępu parsera Java SAX
Odpowiedz
Użyj javax.swing.ProgressMonitorInputStream.
Zakładając, że wiesz, ile artykułów posiadasz, nie możesz po prostu zachować licznika w programie obsługi? Na przykład.
public void startElement (String uri, String localName,
String qName, Attributes attributes)
throws SAXException {
if(qName.equals("article")){
counter++
}
...
}
(nie wiem, czy analizowania „artykuł”, to tylko przykład)
Jeśli nie znasz numeru artykułu z góry, trzeba go najpierw policzyć . Następnie możesz wydrukować status nb tags read/total nb of tags
, powiedz każdy 100 znaczników (counter % 100 == 0
).
A może nawet inny wątek monitoruje postęp. W takim przypadku możesz chcieć zsynchronizować dostęp do licznika, ale nie jest to konieczne, ponieważ nie musi być naprawdę dokładne.
Moje 2 centów
Wymyśliłem to, ale szukałem sposobu na zrobienie tego bez potrzeby liczenia artykułów w pierwszej kolejności. Pomyślałem, że może istnieje sposób, aby dowiedzieć się pozycji parsera w pliku, ponieważ mogę łatwo uzyskać rozmiar pliku. – Danijel
Można uzyskać szacunkową wartość bieżącego wiersza/kolumny w pliku poprzez nadpisanie metody setDocumentLocator
z org.xml.sax.helpers.DefaultHandler/BaseHandler
. Ta metoda jest wywoływana z obiektem, z którego w razie potrzeby można uzyskać przybliżenie bieżącej linii/kolumny.
Edycja: Zgodnie z moją wiedzą, nie ma standardowego sposobu uzyskania absolutnej pozycji. Jestem jednak pewien, że niektóre implementacje SAX oferują tego rodzaju informacje.
Zamknij, ale wtedy musiałbym znać liczbę linii w pliku, prawda? – Danijel
Rzeczywiście. Inna idea mogłaby zostać wskazana przez enigmatyczny EJP. Możesz oszacować postęp, korzystając z zaawansowania w strumieniu wejściowym. Jednak nie jest to również postęp w analizie składniowej, ze względu na potencjalne buforowanie i wyprzedzanie. –
użyję pozycję strumienia wejściowego. Stwórz własną banalną klasę strumieniową, która deleguje/dziedziczy po "prawdziwym" i śledzi czytanie bajtów. Jak mówisz, uzyskanie całkowitego rozmiaru pliku jest łatwe. Nie martwiłbym się o buforowanie, wyprzedzanie itp. - w przypadku dużych plików, takich jak te, jest to karma dla kurcząt. Z drugiej strony ograniczyłbym pozycję do "99%".
Dzięki sugestii EJP o ProgressMonitorInputStream
, w końcu rozszerzyłem FilterInputStream
, dzięki czemu ChangeListener
może być użyte do monitorowania aktualnej pozycji odczytu w terminach bajtów.
Dzięki temu masz lepszą kontrolę, na przykład, aby pokazać wiele pasków postępu dla równoległego odczytu dużych plików xml. Właśnie to zrobiłem.
więc uproszczona wersja do monitorowania strumienia:
/**
* 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);
}
}
nie wie - czy opieki - jak duży strumień bazowy jest, więc trzeba je zdobyć w inny sposób, na przykład z sam plik.
Więc tu idzie uproszczoną wykorzystanie próbki:
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
}
W moim przypadku postępy jego podniesienie ładnie od lewej do prawej bez nieprawidłowych skoków. Dostosuj próg, aby uzyskać optymalną równowagę między wydajnością i reakcją. Zbyt mała i prędkość odczytu może się podwoić na małych urządzeniach, zbyt dużych, a postępy nie byłyby płynne.
Mam nadzieję, że to pomaga. Jeśli znajdziesz błędy lub literówki lub zagłosuj, aby przesłać mi zachęty, możesz je edytować.: D
Doskonale! Dokładnie tego, czego szukałem, dostosuję to, dzięki! :) – Matthieu
Myślę, że to będzie wystarczająco blisko. Dzięki! – Danijel
Czy jakakolwiek odpowiedź może być prostsza ?! :) – Matthieu