Кодировка в запросах из Java

386
26 декабря 2017, 16:55

Есть Tomcat 8 + сервлеты, IDE - Eclipse, ОС - Debian 9. Из сервлетов посылаю GET-запрос на удаленный сервер (указав кодировку в запросе. Предполагается, что это кодировка страницы с удаленного сервера), получаю HTML страничку, парсю ее. Странички содержат русские и украинские буквы. Проблема заключается в том, что даже не смотря на то, что в Эклипсе пришедший ответ читается нормально, отображаются русские буквы, в файл уже пишутся либо ????, либо совсем другие, страшные кракозябры.

Проблема наблюдается с двумя сайтами:

"http://www.meteo.nw.ru/weather/lo_meteod.php", "windows-1251" (исходная кодировка на сайте) и

"http://meteoinfo.ru/mosobl", "UTF-8"(исходная кодировка на этом сайте)

Вот упрощенный код, пытаюсь перекодировать маленький кусочек русского текста:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
    response.getWriter().append("Served at: ").append(request.getContextPath());
    response.getWriter().println();
    StringBuffer response1 = null;
    String url = "http://www.meteo.nw.ru/weather/lo_meteod.php";
    String encoding ="windows-1251";
    URL obj = new URL(url );
    HttpURLConnection connection = (HttpURLConnection) obj.openConnection();
    int status =0;
    int connectionCounter = 0;//не больше трех раз
    try {
        response.getWriter().println("Make the connection with Meteod");
        do {
            if(connectionCounter<3){
                connection = (HttpURLConnection) obj.openConnection();
                connection.setRequestMethod("GET"); 
                if (status!= HttpURLConnection.HTTP_OK) status = connection.getResponseCode();//три раза спрашиваем, но до первого положительного ответа
                else break;
            }
        } while (status!= HttpURLConnection.HTTP_OK);//пока не придет положительный ответ от сервера
    }
    catch(ConnectException e) {
        System.out.println("Ошибка соединения ");
        System.out.println(e.toString());
    }
    catch(IllegalArgumentException e) {
        System.out.println("Ошибка IllegalArgumentException ");
        System.out.println(e.toString());
    }
    catch(IOException e) {
        System.out.println("Общая ошибка ");
        System.out.println(e.toString());
    }
    try{    
        if(status== HttpURLConnection.HTTP_OK){//если пришел
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), encoding));
            String inputLine;
            response1 = new StringBuffer();
            while ((inputLine = in.readLine()) != null)
                response1.append(inputLine);
            in.close();
        }
        else {System.out.println("Сервер не отвечает по адресу "+url); }
    }
    catch(NullPointerException e) {
        System.out.println("NullPointerException ");
        System.out.println(e.toString());
    }
    catch(IOException e) {
        System.out.println("Общая ошибка ");
        System.out.println(e.toString());
    }
    String answer = response1.toString();
    int ind = answer.indexOf("се");
    answer = answer.substring(ind, ind+20);
    response.getWriter().println("The piece of answer:    "+answer);
    System.out.println("Пример распарсенного ответа:  "+answer);
    byte[] winData = answer.getBytes("Cp1251");
    String string = new String(winData,"Cp1251");
    response.getWriter().println("After recoding :    "+answer);
    System.out.println("После перекодиовки:  "+answer);

    File fileMain = new File("C:" + File.separator + "Users" + File.separator + "Alena" + File.separator + "workspace" + File.separator + "AServlet" + File.separator + "test.txt");
    try {
        if(!fileMain.exists()){//проверяем, что если файл не существует то создаем его
            fileMain.createNewFile();
            response.getWriter().println("create an file "+fileMain.getAbsolutePath());
            System.out.println("Создали файл " + fileMain );
        }
        //BufferedWriter outForMain = new BufferedWriter(new OutputStreamWriter((), "<encoding name>"));
        BufferedWriter outForMain = new BufferedWriter(new FileWriter(fileMain, true));//дописывание в конец документа
        try
        {
            response.getWriter().println("Write in file "+fileMain.getAbsolutePath());
            System.out.println("Пишепм");
                outForMain.append(answer);
                outForMain.newLine();
            } finally {
                outForMain.close();
            }
        FileInputStream fstream = new FileInputStream(fileMain);
           BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
        try{
            response.getWriter().println("Read from file "+fileMain.getAbsolutePath());
               String strLine;
               while ((strLine = br.readLine()) != null){
                   response.getWriter().println("The piece of something we read:    "+strLine);
                  System.out.println("Прочли "+strLine);
               }
        } finally {
            br.close();
        }
            }catch (IOException e){
               System.out.println("Ошибка");
            }
}
Answer 1

Ваш код за гранью добра и зла. Приложение, написанное в таком стиле, будет проклято первым человеком, кто попытается в нем что-то исправить либо изменить. Причем этот первый человек, скорее всего, вы сами. Пишите простые и короткие методы, можно использовать стандартные паттерны проектирования. Старайтесь избавиться от дублирующего кода, вынести его в отдельные методы. Это сделает код лаконичным и понятным. Кроме того, хоть иногда заглядывайте в документацию и следите за тем, какие плюшки появляются с выходом новых версий Java и что по этому поводу думают разработчики самого языка. К примеру, конструкция trywithresources появилась еще в 7 версии и ее, согласно документации, настоятельно рекомендуется использовать. А еще есть лямбды... Они тоже способны сократить код. Итого, насколько я понял проблему, у вас трудности с кодировкой при парсе указанных двух сайтов. Я написал вам простой и понятный класс. Из метода мейн сделаете метод, принимающий какие-то параметры, в зависимости от ваших интерфейсов, а дальше простым вызовом 3 методов можно спарсить сайт, записать его в текстовый файл и прочитать из текстового файла. Обратите внимание, что в моем коде для его понимания даже комментировать ничего не надо. И еще... В данном случае мы получаем просто строковую переменную, в которой содержится html страницы. его же записываем в файл, его же читаем и печатаем в консоль. Ведь в вопросе сказано, что проблема именно с кодировкой, мой код решает именно эту проблему. Ежели вам нужно парсить содержимое, то настоятельно рекомендую пользоваться общепринятыми библиотеками. Если это просто страничка HTML - Jsoup, а если нужен JSON - jackson. Обратите внимание, что в том же Jsoup получить стандартный документ для дальнейшей работы можно разными способами, в т.ч. и из обычной строковой переменной, содержащей HTML-страничку: Document document = Jsoup.parse(htmlPage). Причем для сайта есть возможность передать URL либо InputStream и это будет на много конструктивнее. Если же вам нужно вычитывать HTML - из файла, то один из уже указанных вариантов - передать InputStream , но в некоторых случаях может потребоваться передать простую строковую переменную, как я и показал. Удачи

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
class Parser {
    private static final String CHARSET_CP1251 = "Cp1251";
    private static final String CHARSET_UTF8 = "UTF-8";
    private static final String FILE_NAME = "1.txt";
    public static void main(String[] args) throws IOException {
        Parser parser = new Parser();
        String site = parser.parseSite("http://www.meteo.nw.ru/weather/lo_meteod.php", CHARSET_CP1251);
        //String site = parseSite("http://meteoinfo.ru/mosobl", CHARSET_UTF8);
        parser.writeFile(FILE_NAME, site, CHARSET_UTF8);
        String readFile = parser.readFile(FILE_NAME, CHARSET_UTF8);
        System.out.println(readFile);
    }
    private String parseSite (String url, String charset) throws IOException{
        HttpURLConnection connection =(HttpURLConnection)new URL(url).openConnection();
        return toStringByInputStream(new InputStreamReader(connection.getInputStream(), charset));
    }
    private String readFile(String fileName, String charset) throws IOException {
        return toStringByInputStream(new InputStreamReader(new FileInputStream(fileName), charset));
    }
    private String toStringByInputStream (InputStreamReader inputStreamReader) throws IOException {        
        StringBuilder result = new StringBuilder();
        try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
            bufferedReader.lines().forEach((String line)->result.append(line).append("\r\n"));
        }
        return result.toString();
    }
    private void writeFile (String fileName, String text, String charset) throws IOException{
        try (BufferedWriter br = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), charset))) {
            br.write(text);
            br.flush();
        } 
    }
}
READ ALSO
sqlite. Как создать атрибут типа Date

sqlite. Как создать атрибут типа Date

Глядя на уроки на просторах интернета вижу, что добавляют данные в базу используя ContentValuesНо конструктора принимающего тип Date там нет

334
Как обновить один item после изменения в RecyclerView?

Как обновить один item после изменения в RecyclerView?

RecyclerView отображает список CardView с текстом, их можно добавлять и удалятьПроблема вот в чем, я пытаюсь реализовать изменение itema, то есть, на card view по мимо...

220
Подключение к БД Oracle

Подключение к БД Oracle

Есть БД на удаленном сервереПытаюсь подключиться так

290
JavaFX, Рисование на Canvas

JavaFX, Рисование на Canvas

Как рисовать на одном Canvas из разных классов? Проблема в том, что при обращении к canvas из разных классов возвращаются разные ссылки

253