Кодування рядка до 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.