Кодування рядка до UTF-8


190

У мене є рядок з "ñ" характером, і у мене є деякі проблеми з цим. Мені потрібно кодувати цей рядок до кодування UTF-8. Я спробував це таким чином, але він не працює:

byte ptext[] = myString.getBytes();
String value = new String(ptext, "UTF-8");

Як кодувати цей рядок до utf-8?


2
Незрозуміло, що саме ти намагаєшся зробити. Чи правильно myString містить символ ñ і у вас виникають проблеми з перетворенням його в байтовий масив (у такому випадку див. Відповіді Петра і Аміра), або myString пошкоджений і ви намагаєтеся виправити це (у такому випадку див. Відповіді Йоахіма і я)?
— Майкл Боргвардт

Мені потрібно надіслати myString на сервер з кодуванням utf-8, і мені потрібно перетворити символ "-" в кодування utf-8.
— Олексій

1
Що ж, якщо цей сервер очікує UTF-8, тоді вам потрібно надіслати його байти, а не String. Отже, згідно з відповіддю Петра, вкажіть кодування в першому рядку і випустіть другий рядок.
— Майкл Боргвардт

@Michael: Я погоджуюся, що не ясно, в чому полягає справжній намір. Здається, виникає багато питань, коли люди намагаються явні перетворення між рядками та байтами, а не дозволяють {In,Out}putStream{Read,Writ}ersробити це за них. Цікаво, чому?
— tchrist

1
@Michael: Дякую, я думаю, це має сенс. Але це також робить його складніше, ніж потрібно, чи не так? Мені не дуже подобаються мови, які так працюють, і тому намагаюся уникати роботи з ними. Я думаю, що в Java модель Strings символів замість байтів робить речі набагато простішими. Perl і Python також поділяють модель "все - це рядки Unicode". Так, у всіх трьох ви все ще можете отримати байти, якщо працюєте над цим, але на практиці здається, що вам справді потрібно рідко: це досить низький рівень. Плюс це відчуває себе як би чистити кішку в неправильному напрямку, якщо ви знаєте, що я маю на увазі. :)
— tchrist

Відповіді:


140

String Об'єкти в Java використовують кодування UTF-16, яке неможливо змінити.

Єдине, що може мати інше кодування - це byte[]. Тож якщо вам потрібні дані UTF-8, тоді вам потрібні byte[]. Якщо у вас є інформація, Stringщо містить несподівані дані, проблема полягає в попередньому місці, яке неправильно перетворило деякі бінарні дані в String(тобто воно використовувало неправильне кодування).


92
Технічно кажучи, байт [] не має кодування. Кодування байтового PLUS-кодування може дати вам рядок.
— Петро Штібрані

1
@ Петер: правда. Але приєднувати кодування до нього має сенс byte[], це не має сенсу String(якщо тільки кодування не є UTF-16, в цьому випадку це має сенс, але воно все одно зайва інформація).
— Йоахім Зауер

4
String objects in Java use the UTF-16 encoding that can't be modified. Чи є у вас офіційне джерело для цієї цитати?
— Ахмад Хаджар

@AhmadHajjar docs.oracle.com/javase/10/docs/api/java/lang/… : "Платформа Java використовує представлення UTF-16 у масивах char та в класах String і StringBuffer."
— Максі Гіс

173

Як щодо використання

ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(myString)

Дивіться мою дискусію з Петром. Але якщо його припущення щодо питання правильне, ваше рішення все одно не буде ідеєю, оскільки воно повертає ByteBuffer.
— Майкл Боргвардт

8
Але як отримати кодовану рядок? він повертає ByteBuffer
— Alex

7
@Alex: неможливо мати кодовану рядок Java UTF-8. Ви хочете байтів, тому або використовуйте ByteBuffer безпосередньо (навіть це може бути найкращим рішенням, якщо ваша мета - надіслати його через мережеву колекцію) або зателефонувати до масиву (), щоб отримати байт []
— Майкл Боргвардт

2
Ще щось, що може бути корисним, - це використовувати перерахунок Guava Charsets.UTF_8 замість String, який може кинути UnsupportedEncodingException. String -> байти: myString.getBytes(Charsets.UTF_8)і байти -> String new String(myByteArray, Charsets.UTF_8).
— laughing_man

24
Ще краще використовувати StandardCharsets.UTF_8. Доступно на Java 1.7+.
— Кет

81

У Java7 ви можете використовувати:

import static java.nio.charset.StandardCharsets.*;

byte[] ptext = myString.getBytes(ISO_8859_1); 
String value = new String(ptext, UTF_8); 

Це має перевагу перед тим, getBytes(String)що він не декларує throws UnsupportedEncodingException.

Якщо ви використовуєте більш стару версію Java, ви можете оголосити константи шаблону самостійно:

import java.nio.charset.Charset;

public class StandardCharsets {
    public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
    public static final Charset UTF_8 = Charset.forName("UTF-8");
    //....
}

2
Це правильна відповідь. Якщо хтось хоче використовувати рядковий тип даних, він може використовувати його у потрібному форматі. Решта відповідей вказує на тип відформатованого байту.
— Neeraj Shukla

Працює в 6. Дякую.
— Іцік Мауїхас

Правильна відповідь і для мене. Одне, однак, коли я використовував вище, німецький характер змінився на ?. Отже, я використав це: байт [] ptext = myString.getBytes (UTF_8); Значення рядка = новий рядок (ptext, UTF_8); Це спрацювало чудово.
— Farhan Hafeez

3
Зразок коду не має сенсу. Якщо ви вперше перетворите на ISO-8859-1, то цей масив байтів не є UTF-8, тому наступний рядок є абсолютно невірним. Він буде працювати для ASCII рядків, звичайно, але ви можете також зробити просту копію: String value = new String(myString);.
— Алексіс Вілке

76

Використовуйте byte[] ptext = String.getBytes("UTF-8");замість getBytes(). getBytes()використовує так зване "кодування за замовчуванням", яке може бути не UTF-8.


9
@Michael: у нього явно виникають проблеми з отриманням байтів з рядка. Як у програмі getBytes (кодування) відсутня точка? Я думаю, другий рядок є лише для того, щоб перевірити, чи може він його перетворити назад.
— Peter Štibraný

1
Я трактую це як зламану рядок і намагаюся "виправити" її перетворенням на байти та назад (звичайне непорозуміння). Немає фактичних ознак того, що другий рядок просто перевіряє результат.
— Майкл Боргвардт

@Michael, ні, немає, це лише моя інтерпретація. Твоє просто інше.
— Петро Штібрані

1
@Peter: ти маєш рацію, нам потрібно буде уточнити від Алекса, що він насправді означає. Неможливо скасувати протокол, окрім випадків, коли відповідь не буде відредагована ...
— Майкл Боргвардт

33

Язиковий рядок внутрішньо завжди кодується в UTF-16 - але ви дійсно повинні думати про це так: кодування - це спосіб перекладати між рядками та байтами.

Тож якщо у вас є проблема кодування, до моменту появи String це вже пізно виправити. Вам потрібно зафіксувати місце, де ви створюєте цей рядок з файлу, БД або мережевого з'єднання.


1
Поширена помилка вважати, що рядки внутрішньо закодовані як UTF-16. Зазвичай вони є, але якщо, це лише деталь конкретної реалізації класу String. Оскільки внутрішнє зберігання символьних даних недоступне через загальнодоступний API, певна реалізація String може вирішити використовувати будь-яке інше кодування.
— jarnbjo

4
@jarnbjo: API чітко зазначає "Рядок являє собою рядок у форматі UTF-16". Використання будь-якого іншого як внутрішнього формату було б дуже неефективним, а всі фактичні реалізації, які я знаю, використовують UTF-16 всередині. Тож якщо ви не можете навести той, який цього не робить, ви займаєтесь досить абсурдним розчісуванням волосся.
— Майкл Боргвардт

Чи абсурдно розрізняти доступ громадськості та внутрішнє представлення структур даних?
— jarnbjo

6
JVM (наскільки це взагалі стосується VM) використовує UTF-8 для кодування рядків, наприклад, у файлах класу. Реалізація java.lang.String від'єднується від JVM, і я міг легко реалізувати для вас клас, використовуючи будь-яке інше кодування для внутрішнього представлення, якщо це дійсно необхідно, щоб ви зрозуміли, що ваша відповідь невірна. Використання UTF-16 як внутрішнього формату в більшості випадків дуже неефективне, а також щодо споживання пам'яті, і я не розумію, чому, наприклад, реалізація Java для вбудованого обладнання не оптимізувала б для пам'яті замість продуктивності.
— jarnbjo

1
@jarnbjo: І ще раз: до тих пір , як ви не можете дати приклад бетону в віртуальній машині Java, стандартний API реалізація робить внутрішньо використовувати що - то інше , ніж UTF-16 для виконання рядків, моє твердження вірне. І ні, клас String насправді не відокремлений від JVM через такі речі, як intern () та постійний пул.
— Майкл Боргвардт

22

Можна спробувати таким чином.

byte ptext[] = myString.getBytes("ISO-8859-1"); 
String value = new String(ptext, "UTF-8"); 

1
Я зійшов з розуму. Дякую, що спочатку було розроблено байти в "ISO-8859-1".
— Джан Гомен

2
Це неправильно. Якщо ваша рядок включає символи Unicode, перетворення її в 8859-1 призведе до винятку або ще гірше, що ви отримаєте недійсну рядок (можливо, рядок без цих символів з кодовою точкою 0x100 і більше).
— Алексіс Вілке

12

За мить я пережив цю проблему і зумів її вирішити наступним чином

спочатку мені потрібно імпортувати

import java.nio.charset.Charset;

Тоді мені довелося оголосити константу для використання UTF-8іISO-8859-1

private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final Charset ISO = Charset.forName("ISO-8859-1");

Тоді я міг би використовувати його наступним чином:

String textwithaccent="Thís ís a text with accent";
String textwithletter="Ñandú";

text1 = new String(textwithaccent.getBytes(ISO), UTF_8);
text2 = new String(textwithletter.getBytes(ISO),UTF_8);

1
ідеальне рішення.
— Tunde Pizzle

9
String value = new String(myString.getBytes("UTF-8"));

і, якщо ви хочете читати з текстового файлу з кодуванням "ISO-8859-1":

String line;
String f = "C:\\MyPath\\MyFile.txt";
try {
    BufferedReader br = Files.newBufferedReader(Paths.get(f), Charset.forName("ISO-8859-1"));
    while ((line = br.readLine()) != null) {
        System.out.println(new String(line.getBytes("UTF-8")));
    }
} catch (IOException ex) {
    //...
}

2

Я використовую код нижче, щоб кодувати спеціальний символ, вказавши формат кодування.

String text = "This is an example é";
byte[] byteText = text.getBytes(Charset.forName("UTF-8"));
//To get original string from byte.
String originalString= new String(byteText , "UTF-8");

2

Швидке покрокове керівництво, як налаштувати кодування NetBeans за замовчуванням UTF-8. В результаті NetBeans створить усі нові файли в кодуванні UTF-8.

Покрокове керівництво NetBeans, що кодує UTF-8

  • Перейдіть у папку тощо в каталозі встановлення NetBeans

  • Редагуйте файл netbeans.conf

  • Знайдіть рядок netbeans_default_options

  • Додайте -J-Dfile.encoding = UTF-8 всередині лапок у цьому рядку

    (приклад netbeans_default_options="-J-Dfile.encoding=UTF-8":)

  • Перезапустіть NetBeans

Ви встановлюєте NetBeans за замовчуванням, що кодує UTF-8.

Ваші netbeans_default_options можуть містити додаткові параметри всередині лапок. У такому випадку додайте -J-Dfile.encoding = UTF-8 в кінці рядка. Відокремте його пробілом від інших параметрів.

Приклад:

netbeans_default_options = "- J-клієнт -J-Xss128m -J-Xms256m -J-XX: PermSize = 32m -J-Dapple.laf.useScreenMenuBar = true -J-Dapple.awt.graphics.UseQuartz = true -J-Dsun. java2d.noddraw = true -J-Dsun.java2d.dpiaware = true -J-Dsun.zip.disableMemoryMapping = true -J-Dfile.encoding = UTF-8 "

ось посилання для подальшої інформації


0

Це вирішило мою проблему

    String inputText = "some text with escaped chars"
    InputStream is = new ByteArrayInputStream(inputText.getBytes("UTF-8"));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.