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


85

Я пишу веб-програму в Google App Engine. Це дозволяє людям в основному редагувати html-код, який зберігається як .htmlфайл у магазині.

Я використовую fetchData, щоб повернути a byte[]усіх символів у файлі. Я намагаюся надрукувати на html, щоб користувач міг редагувати html-код. Все працює чудово!

Ось моя єдина проблема зараз:

Байтовий масив має деякі проблеми при перетворенні назад у рядок. Розумні цитати та пара символів виходять виглядаючими у стилі фанк. (символи? чи японські символи тощо) Зокрема, це кілька байтів, які я бачу, що мають негативні значення, що спричиняє проблему.

Розумні лапки повертаються як у байтовому масиві, так -108і -109в нього. Чому це і як я можу декодувати від’ємні байти, щоб показати правильне кодування символів?



Привіт, я знаю, що це справді стара публікація, але я стикаюся з подібними проблемами. Я роблю проксі-сервер для ssl. Проблема, з якою я стикаюся, така ж, як і ваша. Я слухаю сокет і отримую дані, InputStreamа потім в byte[]. Тепер, коли я намагаюся перетворити byte[]рядок у рядок (мені потрібно використовувати тіло відповіді для атак), я отримую справді кумедні символи, сповнені розумних лапок і знаків запитання, а що ні. Я вважаю , що ваша проблема в тому ж , як у мене , як ми обидва маємо справу з htmlв byte[]. Чи можете ви, будь ласка, пораду?
— Парул С

До речі, я пішов настільки, щоб знайти кодування своєї системи за допомогою 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 . Це може бути безпечніше, ніж передача кодування як String, як пропонується в інших відповідях.

Наприклад, для кодування UTF-8

String bytesAsString = new String(bytes, StandardCharsets.UTF_8);

1
Це повторення відповіді з 2011 року. -1
— james.garriss

2
@ james.garriss Я не думаю, що це так, оскільки я просто згадую новий конструктор, введений в Java 7, що дозволяє передавати кодування як константу, що, на мій погляд, приємніше і безпечніше, ніж попередній api згадані в попередніх відповідях, де кодування передавалось як рядок, якщо взагалі.
— davnicwil


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
}

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]форматом символу.


Чи можете ви дати більше інформації про те, чому ви це пропонуєте? (Це вирішить проблему? Ви можете сказати, чому це вирішує?) Дякую!
— Дін Дж.

Це залежить від вашої мети. Наприклад, я хотів зберегти байтовий масив точно так само, як формат, який ви можете бачити під час налагодження, приблизно такий: [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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.