Задача сохранить в файл все ссылки в указанном URL. Во время чтения через BufferedReader вылетает NullPointerException при использовании цикла do while
в строке if (buffer.indexOf ("<a ")> 0) }
,а при использовании for
все работает, хотя по идее оба цикла выполняют одно и то же. Подскажите почему так. Ранее без проблем использовал как один, так и другой цикл.
Вылетает:
public static void getLinks(String link, String file) {
StringBuilder sb = new StringBuilder();
try {
URL url = new URL(link);
HttpURLConnection connect = (HttpURLConnection) url.openConnection();
BufferedReader input = new BufferedReader(new InputStreamReader(connect.getInputStream()));
String buffer = "";
String text = "";
do {
buffer = input.readLine();
if (buffer.indexOf("<a ") > 0) { // NullPointerException
if (buffer.indexOf("</a") > 0) {
sb.append(buffer.substring(buffer.indexOf("<a "), buffer.indexOf("</a") + 4))
.append(System.lineSeparator());
text = "";
} else {
text = buffer.substring(buffer.indexOf("<a "));
}
} else if (text.length() > 0) {
if (buffer.indexOf("</a") > 0) {
sb.append(text).append(text + buffer.substring(1, buffer.indexOf("</a") + 4))
.append(System.lineSeparator());
text = "";
} else {
text += buffer;
}
}
} while (buffer != null);
} catch (IOException e) {
System.out.println(e);
}
try (ObjectOutputStream links = new ObjectOutputStream(new FileOutputStream(file))) {
links.writeObject(sb.toString());
System.out.println("File " + file + " was saved!");
} catch (IOException e) {
System.out.println("Error save file!");
}
}
Работает:
public static void getLinks(String link, String file) {
StringBuilder sb = new StringBuilder();
try {
URL url = new URL(link);
HttpURLConnection connect = (HttpURLConnection) url.openConnection();
BufferedReader input = new BufferedReader(new InputStreamReader(connect.getInputStream()));
String buffer = "";
String text = "";
for (; (buffer = input.readLine()) != null;) { // работает
if (buffer.indexOf("<a ") > 0) {
if (buffer.indexOf("</a") > 0) {
sb.append(buffer.substring(buffer.indexOf("<a "), buffer.indexOf("</a") + 4))
.append(System.lineSeparator());
text = "";
} else {
text = buffer.substring(buffer.indexOf("<a "));
}
} else if (text.length() > 0) {
if (buffer.indexOf("</a") > 0) {
sb.append(text).append(text + buffer.substring(1, buffer.indexOf("</a") + 4))
.append(System.lineSeparator());
text = "";
} else {
text += buffer;
}
}
}
} catch (IOException e) {
System.out.println(e);
}
try (ObjectOutputStream links = new ObjectOutputStream(new FileOutputStream(file))) {
links.writeObject(sb.toString());
System.out.println("File " + file + " was saved!");
} catch (IOException e) {
System.out.println("Error save file!");
}
}
Ваш do-while
работает не так же, как for
: в for
перед каждой итерацией проверяется, что считанный buffer
не равен null
. В do-while
же эта проверка происходит после того, как buffer
уже был использован, и, по сути, поэтому проверка лишена смысла, так как если buffer
равен null
, то об этом станет известно уже в строке if (buffer.indexOf("<a ") > 0)
про проброшенному NullPointerException
, и до while
дело не дойдёт.
Имеет смысл do-while
заменить на while
:
while ((buffer = input.readLine()) != null)
При использовании Java 8 можно пройтись по всем строкам с использованием for-each.
Например, так:
Stream<String> stream = input.lines();
for (String buffer : (Iterable<String>)stream::iterator) { ... }
Или хотя бы так:
for (String buffer : input.lines().collect(Collectors.toList())) { ... }
Построчное чтение файла предполагает чтение строки и проверку на каждой итерации:
while (true) {
buffer = input.readLine();
if (buffer == null) break;
// ...
}
Или:
do {
buffer = input.readLine();
// buffer может быть null здесь, поэтому обязательна проверка:
if (buffer == null) break;
// ...
} while (buffer != null); // Из-за проверки выше, проверка здесь не имеет смысла. Выражение всегда true
Как верно заметил @Regent, этот код также можно записать как:
while ((buffer = input.readLine()) != null) {}
Хотя на самом деле, ваш цикл for
является полным ему аналогом - for(;cond;);
эквивалентен while(cond);
Однако многие styleguides прямо запрещают использование оператора присваивания в условиях.
Если чтение первой строки считать инициализацией цикла, то можно записать этот код как:
for(buffer = input.readLine(); buffer != null; buffer = input.readLine()){
// ...
}
Однако здесь, очевидно, налицо дублирование кода.
В Java, когда нам нужно перебрать элементы некоторой коллекции(например, строк в файле) мы описываем итератор.
Давайте попробуем:
public class BufferedReaderIterator implements Iterable<String> {
private BufferedReader input;
public BufferedReaderIterator(BufferedReader input) {
this.input = input;
}
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
@Override
public boolean hasNext() {
try {
input.mark(1);
if (input.read() < 0) return false;
input.reset();
return true;
}
catch (IOException e) {return false; }
}
@Override
public String next() {
try { return input.readLine(); }
catch (IOException e) {return null; }
}
@Override
public void remove() {throw new UnsupportedOperationException(); }
};
}
И вот как теперь будет выглядеть первоначальный цикл:
for(String buffer: new BufferedReaderIterator(input)){
// ...
}
Мы избавились и от дублирования, и от присваивания.
В качестве альтернативы можно воспользоваться готовым решением, например: org.apache.commons.io.LineIterator.
Правда с ним придётся обрабатывать исключения.
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Есть текстовый файл, который содержит целые числа, каждое с новой строки:
Клиент - сервер на RMI При старте под intelij idea, на стороне выбрасывается исключение