2015-07-30 3 views
1

У меня есть строка, которая возвращается парсером HTML Jericho и содержит некоторый русский текст. Согласно source.getEncoding() и заголовку соответствующего HTML-файла, кодировка представляет собой Windows-1251.Как преобразовать текст Windows-1251 в нечто читаемое?

Как я могу преобразовать эту строку в нечто читаемое?

Я попытался это:

import java.io.UnsupportedEncodingException; 

public class Program { 
    public void run() throws UnsupportedEncodingException { 
     final String windows1251String = getWindows1251String(); 
     System.out.println("String (Windows-1251): " + windows1251String); 
     final String readableString = convertString(windows1251String); 
     System.out.println("String (converted): " + readableString); 
    } 
    private String convertString(String windows1251String) throws UnsupportedEncodingException { 
     return new String(windows1251String.getBytes(), "UTF-8"); 
    } 
    private String getWindows1251String() { 
     final byte[] bytes = new byte[] {32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}; 
     return new String(bytes); 
    } 
    public static void main(final String[] args) throws UnsupportedEncodingException { 
     final Program program = new Program(); 
     program.run(); 
    } 
} 

Переменная bytes содержит данные, приведенные в моем отладчика, это результат net.htmlparser.jericho.Element.getContent().toString().getBytes(). Я просто копировал и вставлял этот массив здесь.

Это не работает - readableString содержит мусор.

Как это исправить, т.е. е. убедитесь, что строка Windows-1251 правильно декодирована?

Update 1 (30.07.2015 12:45 MSK): При изменении кодировки в вызове в convertString к Windows-1251, ничего не меняется. См. Снимок экрана ниже.

Screenshot

Update 2: Еще одна попытка:

Second screenshot

Update 3 (30.07.2015 14:38): тексты, которые мне нужно декодировать соответствуют текстам в раскрывающемся списке, показанном ниже.

Expected result

обновление 4 (30.07.2015 14:41): Детектор кодирования (код ниже) говорит, что кодирование не Windows-1251, но UTF-8.

public static String guessEncoding(byte[] bytes) { 
    String DEFAULT_ENCODING = "UTF-8"; 
    org.mozilla.universalchardet.UniversalDetector detector = 
     new org.mozilla.universalchardet.UniversalDetector(null); 
    detector.handleData(bytes, 0, bytes.length); 
    detector.dataEnd(); 
    String encoding = detector.getDetectedCharset(); 
    System.out.println("Detected encoding: " + encoding); 
    detector.reset(); 
    if (encoding == null) { 
     encoding = DEFAULT_ENCODING; 
    } 
    return encoding; 
} 
+1

Вы попробовали 'new String (bytes," Windows-1251 ")'? –

+0

Я подозреваю, что ваш конструктор String() должен указать кодировку, используемую в вашем массиве байтов, в противном случае вы подпадаете под JVM-кодирование для вашей среды –

+0

@FlorianSchaetz Да, см. Обновление 1. –

ответ

2

Я исправил эту проблему, изменив фрагмент кода, который читал текст с веб-сайта.

private String readContent(final String urlAsString) { 
    final StringBuilder content = new StringBuilder(); 
    BufferedReader reader = null; 
    InputStream inputStream = null; 
    try { 
     final URL url = new URL(urlAsString); 
     inputStream = url.openStream(); 
     reader = 
      new BufferedReader(new InputStreamReader(inputStream); 

     String inputLine; 
     while ((inputLine = reader.readLine()) != null) { 
      content.append(inputLine); 
     } 
    } catch (final IOException exception) { 
     exception.printStackTrace(); 
    } finally { 
     IOUtils.closeQuietly(reader); 
     IOUtils.closeQuietly(inputStream); 
    } 
    return content.toString(); 
} 

Я изменил линию

new BufferedReader(new InputStreamReader(inputStream); 

в

new BufferedReader(new InputStreamReader(inputStream, "Windows-1251")); 

, а затем он работал.

+0

Дело в том, что читатели уже выполняют преобразование из 'byte' в' char' внутренне. Это также основное различие между читателем и потоком. На данный момент ваши данные повреждены. Хорошее решение. – bvdb

3

(В свете обновлений я удалил свой оригинальный ответ и начал снова)

Текст, который появляется

пїЅпїЅпїЅпїЅпїЅпїЅ 

является точным декодирования этих байт значения

-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67 

(Заполненный с обоих концов 32, который является пространством.)

Так как

1) Текст мусора или

2) Текст должен выглядеть или

3) Кодирование не для Windows 1215

Эта строка, в частности, неверна

return new String(windows1251String.getBytes(), "UTF-8"); 

Извлечение байтов из tring и построение новой строки из этого не является способом «преобразования» между кодировками. Как входная строка, так и выходная строка используют внутреннюю кодировку UTF-16 (и вам обычно не нужно об этом знать или заботиться). Единственное время, когда другие кодировки вступают в игру, - это когда текстовые данные хранятся в OUTSIDE строкового объекта, то есть в вашем исходном массиве байтов. Преобразование происходит, когда String построена, а затем выполняется. Нет никакого преобразования из одного типа String в другой - они все одинаковы.

Тот факт, что это

return new String(bytes); 

делает то же самое, как это

return new String(bytes, "Windows-1251"); 

предполагает, что Windows-1251 является кодировка платформы по умолчанию. (Что дополнительно поддерживается вашим часовым поясом MSK)

+0

+1; Я могу подтвердить, что 'byte []' отображается правильно. Я проверил их на кодовой странице Windows-1251. (byte -17 = int 239 = 0xEF = char 'п') https://en.wikipedia.org/wiki/Windows-1251 – bvdb

1

Просто, чтобы убедиться, что вы понимаете на 100%, как работает java с char и byte.

byte[] input = new byte[1]; 

// values > 127 become negative when you put them in an array. 
input[0] = (byte)239; // the array contains value -17 now. 

// but all 255 values are preserved. 
// But if you cast them to integers, you should use their unsigned value. 
// (casting alone isn't enough). 
int output = input[0] & 0xFF; // output is 239 again 

// you shouldn't cast directly from a single-byte to a char. 
// because: char is 16-bit ; but you only want to use 1 byte ; unfortunately your negative values will be applied in the 2nd byte, and break it. 
char corrupted = (char) input[0]; // char-code: 65519 (2 bytes are used) 
char corrupted = (char) ((int)input[0]); // char-code: 66519 (2 bytes are used) 

// just casting to an integer/character is ok for values < 0x7F though 
// values < 0x7F are always positive, even when casted to byte 
// AND the first 7-bits in any ascii-encodings (e.g. windows-1251) are identical. 
byte simple = (byte) 'a'; 
char chr = (char) ascii_LT_7F; // will result in 'a' again 

// But it's still more reliable to use the & 0xFF conversion. 
// Because it ensures that your character can never be greater than char code 255 (a single byte), even when the byte is unexpectedly negative (> 0x7F). 
char chr = (char) ((byte)simple & 0xFF); // also results in 'a' 

// for value 239 (which is 0xEF) it's impossible though. 
// a java char is 16-bit encoded internally, following the unicode character set. 
// characters 0x00 to 0x7F are identical in most encodings. 
// but e.g. 0xEF in windows-1251 does not match 0xEF in UTF-16. 
// so, this is a bad idea. 
char corrupted = (char) (input[0] & 0xFF); 

// And that's something you can only fix by using encodings. 
// It's good practice to use encodings really just ALWAYS. 
// the encoding indicates what your bytes[] are encoded in NOW. 
// your bytes will be converted to 16-bit characters. 
String text = new String(bytes, "from-encoding"); 

// if you want to change that text back to bytes, use an encoding !! 
// this time the encoding specifies is the TARGET-ENCODING. 
byte[] bytes = text.getBytes("to-encoding"); 

Надеюсь, это поможет.

Что касается отображаемых значений: Я могу подтвердить, что байт [] отображается правильно. Я проверил их на кодовой странице Windows-1251. (byte -17 = int 239 = 0xEF = char 'п')

Другими словами, ваши байтовые значения неверны или это другое кодирование источника.

 Смежные вопросы

  • Нет связанных вопросов^_^