Я пишу SAX-парсер в Java для анализа 2,5-гигабайтного XML-файла статей в википедии. Есть ли способ отслеживать ход разбора в Java?Мониторинг прогресса анализатора Java SAX
ответ
Используйте javax.swing.ProgressMonitorInputStream.
Предполагая, что вы знаете, сколько статей у вас есть, не можете ли вы просто оставить счетчик в обработчике? Например.
public void startElement (String uri, String localName,
String qName, Attributes attributes)
throws SAXException {
if(qName.equals("article")){
counter++
}
...
}
(я не знаю, вы разбор «статьи», это просто пример)
Если вы не знаете номер статьи заранее, вам нужно будет считать его первым , Затем вы можете распечатать статус nb tags read/total nb of tags
, скажем, каждые 100 тегов (counter % 100 == 0
).
Или у вас есть еще одна нить, отслеживающая прогресс. В этом случае вам может понадобиться синхронизировать доступ к счетчику, но не обязательно, учитывая, что он не должен быть действительно точным.
Мои 2 цента
Я понял это, но я искал способ сделать это, не считая сначала статей. Я подумал, что, возможно, был способ выяснить позицию парсера в файле, потому что я могу легко получить размер файла. – Danijel
Вы можете получить оценку текущей строки/столбца в файле путем переопределения метода setDocumentLocator
из org.xml.sax.helpers.DefaultHandler/BaseHandler
. Этот метод вызывается с объектом, из которого вы можете получить приближение текущей строки/столбца, когда это необходимо.
Редактировать: Насколько мне известно, стандартного способа получить абсолютное положение нет. Тем не менее, я уверен, что некоторые реализации SAX предлагают такую информацию.
Закрыть, но тогда мне нужно будет узнать количество строк в файле, не так ли? – Danijel
Действительно. Другая идея могла бы быть указана загадочным EJP. Вы можете оценить прогресс, используя продвижение во входном потоке. Тем не менее, это не прогресс в анализе, из-за потенциальной буферизации и просмотра. –
Я хотел бы использовать положение входного потока. Создайте свой собственный тривиальный класс потока, который делегирует/наследует от «реального» и отслеживает чтение байтов. Как вы говорите, получить общий размер файла легко. Я бы не беспокоился о буферизации, поиске и т. Д. - для больших файлов, подобных этим, это курица. С другой стороны, я бы ограничил позицию «99%».
Благодаря предложению EJP ProgressMonitorInputStream
, в конце я продлил FilterInputStream
, так что ChangeListener
может использоваться для контроля текущего местоположения чтения в терминах байтов.
С этим вы имеете более тонкое управление, например, чтобы показать несколько индикаторов выполнения для параллельного чтения больших файлов xml. Это именно то, что я сделал.
Таким образом, упрощенная версия отслеживаемых потока:
/**
* 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);
}
}
Он не знает - или ухода - как большой основной поток, так что вам нужно, чтобы получить его другим способом, например, от самого файла.
Итак, здесь идет упрощенное использование образца:
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
}
В моем случае подвижки поднимают красиво слева направо без аномальных скачков. Отрегулируйте пороговое значение для оптимального баланса между производительностью и быстротой реагирования. Слишком мало, и скорость чтения может более чем удваиваться на небольших устройствах, слишком велика, и прогресс не будет плавным.
Надеюсь, это поможет. Не стесняйтесь редактировать, если вы обнаружили ошибки или опечатки, или проголосуйте, чтобы прислать мне некоторые поощрения!: D
Отлично! Именно то, что я искал, я приспособию это, спасибо! :) – Matthieu
Я думаю, что это будет достаточно близко. Благодаря! – Danijel
Может ли любой ответ быть проще? :) – Matthieu