Преобразование байтового массива в String (Java)

85

Я пишу веб-приложение в Google App Engine. Он позволяет людям в основном редактировать html-код, который сохраняется в виде .htmlфайла в хранилище blobstore.

Я использую fetchData для возврата byte[]всех символов в файле. Я пытаюсь распечатать HTML-код, чтобы пользователь мог отредактировать HTML-код. Все отлично работает!

Вот теперь моя единственная проблема:

У байтового массива возникают проблемы при преобразовании обратно в строку. Умные цитаты и пара персонажей выглядят напуганными. (? или японские символы и т. д.) В частности, я вижу несколько байтов с отрицательными значениями, которые вызывают проблему.

Умные цитаты возвращаются , как -108и -109в массиве байт. Почему это так и как я могу декодировать отрицательные байты, чтобы показать правильную кодировку символов?

Джош
источник
Дубликат stackoverflow.com/questions/1536054/…
james.garriss 05
Привет, я знаю, что это действительно старый пост, но у меня похожие проблемы. Я делаю прокси-сервер для ssl. Проблема, с которой я столкнулся, такая же, как и ваша. Я слушаю сокет и получаю данные, InputStreamа затем в него byte[]. Теперь, когда я пытаюсь преобразовать byte[]в String (мне нужно использовать тело ответа для атак), я получаю действительно забавные символы, полные умных кавычек, вопросительных знаков и прочего. Я считаю , что ваша проблема в том же , как у меня , как мы оба имеем дело с htmlв byte[]. Вы можете посоветовать?
Parul S
Между прочим, я попытался найти кодировку моей системы с помощью Sytem.properties и обнаружил, что это "Cp1252". Я использовал, String str=new String(buffer, "Cp1252");но без помощи.
Parul S

Ответы:

141

Массив байтов содержит символы в специальной кодировке (которую вам следует знать). Способ преобразования его в строку:

String decoded = new String(bytes, "UTF-8");  // example for one encoding type

Кстати, необработанные байты могут отображаться как отрицательные десятичные числа только потому, что тип данных java byteподписан, он охватывает диапазон от -128 до 127.


-109 = 0x93: Control Code "Set Transmit State"

Значение (-109) - это непечатаемый управляющий символ в UNICODE. Таким образом, UTF-8 не является правильной кодировкой для этого символьного потока.

0x93в «Windows-1252» - это «умная цитата», которую вы ищете, поэтому имя Java этой кодировки - «Cp1252». В следующей строке представлен тестовый код:

System.out.println(new String(new byte[]{-109}, "Cp1252")); 
Андреас Долк
источник
5
Я пробовал использовать UTF-8, но все равно получилось? Почему он не находит соответствия для этих отрицательных значений?
Джош
Однако 0x93 является допустимым байтом продолжения в UTF-8 - наличие этого байта исключает, что он является UTF-8, только если он не идет после байта с установленными первыми двумя битами.
Ник Джонсон
1
@Josh Andreas объясняет, почему - потому что byteтип данных Java подписан. «Отрицательные» значения - это просто байты с самым старшим набором байтов. Он также объясняет, какой наиболее вероятный набор символов вам следует использовать - Windows-1252. Однако вы должны знать, какой набор символов использовать из контекста или соглашения, без необходимости гадать.
Ник Джонсон
25

Java 7 и выше

Вы также можете передать желаемую кодировку Stringконструктору в виде Charsetконстанты из StandardCharsets . Это может быть безопаснее, чем передача кодировки как a String, как предлагается в других ответах.

Например, для кодировки UTF-8

String bytesAsString = new String(bytes, StandardCharsets.UTF_8);
Давниквиль
источник
1
Это повторение ответа 2011 года. -1
james.garriss
2
@ james.garriss Я не думаю, что это так, поскольку я просто упоминаю новый конструктор, представленный в java 7, позволяющий передавать кодировку как константу, что, на мой взгляд, лучше и безопаснее, чем предыдущий api упоминалось в более ранних ответах, где кодировка была передана как строка, если вообще была.
davnicwil
11

Вы можете попробовать это.

String s = new String(bytearray);
Мухаммад Аамир Али
источник
9
Вы можете попробовать ... но почти во всех случаях это не удастся.
Raedwald
5
public class Main {

    /**
     * Example method for converting a byte to a String.
     */
    public void convertByteToString() {

        byte b = 65;

        //Using the static toString method of the Byte class
        System.out.println(Byte.toString(b));

        //Using simple concatenation with an empty String
        System.out.println(b + "");

        //Creating a byte array and passing it to the String constructor
        System.out.println(new String(new byte[] {b}));

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Main().convertByteToString();
    }
}

Выход

65
65
A
Ади Сембиринг
источник
5
public static String readFile(String fn)   throws IOException 
{
    File f = new File(fn);

    byte[] buffer = new byte[(int)f.length()];
    FileInputStream is = new FileInputStream(fn);
    is.read(buffer);
    is.close();

    return  new String(buffer, "UTF-8"); // use desired encoding
}
Craig
источник
3
Этот код приведет к утечке ресурса, если вызовет readисключение.
Raedwald
4

Я предлагаю Arrays.toString(byte_array);

Это зависит от вашей цели. Например, я хотел сохранить массив байтов в точном соответствии с форматом, который вы видите во время отладки, который выглядит примерно так: [1, 2, 3]если вы хотите сохранить точно такое же значение без преобразования байтов в символьный формат, Arrays.toString (byte_array)сделайте это. Но если вы хотите сохранить символы вместо байтов, вы должны использовать String s = new String(byte_array). В этом случае sравно эквиваленту [1, 2, 3]в формате символа.

Спрашивающий
источник
Не могли бы вы подробнее объяснить, почему вы предлагаете это? (Решит ли это проблему? Можете сказать, почему она решает?) Спасибо!
Dean J
Это зависит от вашей цели. Например, я хотел сохранить массив байтов точно так же, как формат, который вы видите во время отладки, который выглядит примерно так: [1, 2, 3] Если вы хотите сохранить точно такое же значение без преобразования байтов в символьный формат, Arrays.toString (byte_array) делает это. Но если вы хотите сохранить символы вместо байтов, вы должны использовать String s = new String (byte_array). В этом случае s равно эквиваленту [1, 2, 3] в формате символа.
Вопрос
@sas, вы должны добавить эту информацию в свой ответ (отредактировав его), а не в качестве комментария. Как правило, в SO вы всегда должны иметь в виду, что комментарии могут быть удалены в любой момент - действительно важная информация должна быть в самом ответе.
Jeen Broekstra
3

Предыдущий ответ от Andreas_D хорош. Я просто собираюсь добавить, что везде, где вы показываете вывод, будет шрифт и кодировка символов, и он может не поддерживать некоторые символы.

Чтобы определить, является ли проблема Java или вашим дисплеем, сделайте следующее:

    for(int i=0;i<str.length();i++) {
        char ch = str.charAt(i);
        System.out.println(i+" : "+ch+" "+Integer.toHexString(ch)+((ch=='\ufffd') ? " Unknown character" : ""));
    }

Java отобразит любые символы, которые она не может понять, в 0xfffd - официальный символ для неизвестных символов. Если вы видите "?" в выводе, но он не сопоставлен с 0xfffd, проблема заключается в шрифте или кодировке дисплея, а не в Java.

Саймон Г.
источник