I have a problem with the assembly of the project:
D:…\Main.java:112: error: unmappable character (0x98) for encoding
windows-1251
robot.getBrowserControl().findElement(By.xpath(«//div[.=’НАПИСАТЬ’]»)).click();
This error in line:
robot.getBrowserControl().findElement(By.xpath("//div[.='НАПИСАТЬ']")).click();
Images:
error, IDEA settings . Ths bottom-right is selected UTF-8. I’m not sure, but maybe it’s like the symbol «И»
img errors
This my gradle file
plugins {
id 'java'
}
group 'ru.grbi3yh.processthesefiles'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
baseName = 'ProcessthFiles'
}
apply plugin: 'application'
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '2.41.0'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
}
I have a problem with the assembly of the project:
D:…\Main.java:112: error: unmappable character (0x98) for encoding
windows-1251
robot.getBrowserControl().findElement(By.xpath(«//div[.=’НАПИСАТЬ’]»)).click();
This error in line:
robot.getBrowserControl().findElement(By.xpath("//div[.='НАПИСАТЬ']")).click();
Images:
error, IDEA settings . Ths bottom-right is selected UTF-8. I’m not sure, but maybe it’s like the symbol «И»
img errors
This my gradle file
plugins {
id 'java'
}
group 'ru.grbi3yh.processthesefiles'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
baseName = 'ProcessthFiles'
}
apply plugin: 'application'
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '2.41.0'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
}
У меня проблема со сборкой проекта:
D:… \ Main.java:112: ошибка: не отображаемый символ (0x98) для кодирования windows-1251
. Robot.getBrowserControl() findElement(By.xpath(«// DIV [=’НАПИСАТЬ.]»)) Нажмите ().
Эта ошибка в строке:robot.getBrowserControl().findElement(By.xpath("//div[.='НАПИСАТЬ']")).click();
Изображения: ошибка, настройки IDEA. Это справа внизу выбран UTF-8. Я не уверен, но, возможно, это похоже на символ «И»
ошибки img
Это мой подлый файл
plugins {
id 'java'
}
group 'ru.grbi3yh.processthesefiles'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
baseName = 'ProcessthFiles'
}
apply plugin: 'application'
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '2.41.0'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
}
I have a problem with the assembly of the project:
D:…\Main.java:112: error: unmappable character (0x98) for encoding
windows-1251
robot.getBrowserControl().findElement(By.xpath(«//div[.=’НАПИСАТЬ’]»)).click();
This error in line:
robot.getBrowserControl().findElement(By.xpath("//div[.='НАПИСАТЬ']")).click();
Images:
error, IDEA settings . Ths bottom-right is selected UTF-8. I’m not sure, but maybe it’s like the symbol «И»
img errors
This my gradle file
plugins {
id 'java'
}
group 'ru.grbi3yh.processthesefiles'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
baseName = 'ProcessthFiles'
}
apply plugin: 'application'
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '2.41.0'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
}
Setting encoding option for Java compilation should help:
compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
As an alternative way to solve this error:
«error: unmappable character (0x98) for encoding windows-1251»
you can also change the default encoding inside Custom VM Options...
:
by adding, e.g.:
-Dfile.encoding=UTF-8
Help | Edit Custom VM Options, then restart IntelliJ IDEA.
Последнее изменение: 17 октября 2012г.
Честно сказать, эта статья тоже из серии «ликбеза» – я считал, что об этом должны знать все.
Практика показывает, что я ошибался. Долго думал, куда бы этот материал добавить – то ли действительно
в статью «Ликбез», то ли в первую часть
«Вавилонского столпотворения». В итоге решил вынести в отдельную статью.
Вавилонское столпотворение. Часть 4. Проза жизни: компиляция и вывод в консоль
Итак, речь у нас пойдет о таких, казалось бы, примитивных процессах, как компиляция и вывод в консоль. Крайне
рекомендую ознакомиться со статьями «Ликбез» и
«Вавилонское столпотворение. Часть 1. Кодировки» – это необходимо для понимания данного
материала. По сложности же кода дальше уровня «Hello, World» мы не пойдем.
Компиляция «в лоб»
Для начала простое упражнение. Возьмите вот этот файл –
HelloWorldUTF8.java – если у вас Windows, и вот этот –
HelloWorldCp866.java – если у вас Linux или Mac. Скомпилируйте и запустите (все команды я привожу
для Windows):
javac -cp . -d . HelloWorldUTF8.java java -cp . test.HelloWorldUTF8
Надеюсь, вы понимаете, что делают эти команды. Если нет – читайте вот это: «Ликбез».
В свой хрустальный шар я вижу, что в консоли у вас ничего хорошего. Во-первых, под Linux-ом я ожидаю кучу
предупреждений еще на стадии компиляции – 8 штук. Во-вторых, при выполнении у меня (под Windows)
– вот так:
╨Ч╨┤╤А╨░╨▓╤Б╤В╨▓╤Г╨╣, ╨Ь╨╕╤А!
Под Linux должны быть прямоугольнички вместо букв.
Теперь поменяйтесь – возьмите файл, который я предназначил для другой операционки. И то же самое
– скомпилируйте и запустите:
javac -cp . -d . HelloWorldCp866.java java -cp . test.HelloWorldCp866
Вот так уже намного лучше. К сожалению, не могу ручаться, что в Linux тоже все хорошо, ибо настраивается эта
ОС по-разному. Но при стандартной настройке должен быть виден нормальный русский текст:
Здравствуй, Мир!
Упражнение окончено.
Что можно из него вынести? Основное, что мы увидели – результат компиляции зависит от операционной
системы. Что нас, понятное дело, устраивать не может.
Но и это еще не все. Вот то, что получилось при компиляции (необходимая версия Java – 6.0+): test_windows.zip и test_linux.zip. Запустим это
безобразие на других системах, отличных от той, где они компилировались. Содержимое
, запущенное под Linux, дает такой результат:
test_windows.zip
$ java -cp . test.HelloWorldUTF8 Здравствуй, РњРёСЂ! $ java -cp . test.HelloWorldCp866 ‡¤а ўбвўг©, ЊЁа!
Ну и код из test_linux.zip
под windows дает вот что:
java -cp . test.HelloWorldUTF8 ╟фЁртёЄтєщ, ╠шЁ! java -cp . test.HelloWorldCp866 ??????, ???!
То есть – во всех случаях бредятина и ничего похожего на то, что было под другой OС. Никакой
переносимости. Что нас тоже никак не может устраивать.
В общем, ситуация ясна. Имеется проблема. Надо понять, как ее решать. А для этого необходимо понимание
происходящего.
Что происходит?
Для начала я открою страшную тайну. В разных системах используются разные кодировки по умолчанию. В Windows
это Cp1251
, в Linux – как правило, UTF-8
. Приведенные мной
примеры рассчитаны именно на это.
Второй момент, о котором знает существенно меньшее количество разработчиков. В Windows кодировка
консоли не совпадает с кодировкой системы! Консоль по историческим причинам имеет кодировку
Cp866
, также известную под неофициальными именами DOS
и OEM
.
Третий момент. Компилятор при разборе исходного кода по умолчанию использует кодировку системы. В Windows
– Cp1251
, в Linux – UTF-8
.
Четвертый момент. Посмотрите на класс java.lang.System
, а точнее – на его переменную
java.lang.System.out
. Она имеет тип PrintStream
. Т.е. –
оперирует байтами. А следовательно, тоже использует какую-то кодировку.
И вот тут-то начинается веселье. Кодировка, используемая при выводе в консоль –
системная! А вовсе не кодировка консоли, как можно было бы предположить. И если они не совпадают
– жди неприятностей.
Понятно? Тогда вопрос. Почему же при компиляции исходника в Cp866
под Windows и его последующем
исполнении там же мы видим нормальный текст??? Кодировки-то не совпадают!
Отчасти этот факт и побудил меня вообще написать эту статью. Недавно меня на полном серьезе убеждали в одном
из форумов, что для компиляции и нормального вывода в консоль необходимо и достаточно, чтобы исходник был
в кодировке консоли. Более того, как вы только что видели, это вроде бы действительно так. Так?
Нет. И я это сейчас продемонстрирую. Возьмем еще один исходник в Cp866
, почувствуем себя
немножко логопедом, отрабатывающим шипящие, и заменим уже набившую оскомину фразу «Здравствуй,
мир!» на более веселую – «МыШка суШек насуШила!». Вот этот исходник:
HelloWorldCp866_2.java. Как и прежде – компилируем и исполняем
(это уже только под Windows):
javac -cp . -d . HelloWorldCp866_2.java HelloWorldCp866_2.java:6: warning: unmappable character for encoding Cp1251 System.out.println("Мы?ка су?ек насу?ила!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding Cp1251 System.out.println("Мы?ка су?ек насу?ила!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding Cp1251 System.out.println("Мы?ка су?ек насу?ила!"); ^ 3 warnings java -cp . test.HelloWorldCp866_2 Мы?ка су?ек насу?ила!
Как вам результат?
Не звучат шипящие у пациента. Причем уже на стадии компиляции. Почему? Ма-а-аленькая подсказка. Код буквы
«Ш» – 0x98
. Вам это ничего не напоминает? Мне это напоминает потерявшуюся букву «И» в статье о кодировках. У нее в составе
байтов тоже было значение 0x98
.
Догадались, что произошло? Двойная ошибка. Сначала Cp866
читается как системная
. В нижней части они совпадают, так что программный код не повреждается. А вот верхние части
Cp1251
таблицы (выше 127 символов) различаются, и серьезно. В результате символы, которые после компиляции
сохраняются в байт-коде, не имеют ничего общего с тем, что было в исходнике.
Однако при выводе на консоль происходит ровно обратное. Символы с помощью кодировки Cp1251
превращаются в байты. А эти байты соответствуют Cp866
. Только поэтому на консоль выводится
правильный текст.
И все было бы замечательно, если бы не отсутствие в Cp1251
символа с кодом 0x98
, на
который попадает буква «Ш». Декодер встречает байт 0x98
, не находит такого символа
и выдает предупреждение: unmappable character for encoding Cp1251. Буквально –
неотображаемый символ для кодировки Cp1251
. А сам символ замещает на «?». Это мы уже
видели. Вот «Ш» и пропала.
И еще более показательна компиляция того же исходника под Linux:
$ javac -cp . -d . HelloWorldCp866_2.java HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^ HelloWorldCp866_2.java:6: warning: unmappable character for encoding UTF8 System.out.println("�똪� �㘥� ���㘨��!"); ^
Даже сказать нечего. Если под Windows, когда однобайтовая Cp866
расценивается как однобайтовая
же Cp1251
, не отображается только «Ш», то под Linux, при попытке расценить
как многобайтовую
Cp866UTF-8
, текст вообще не распознался. Вылез какой-то странный
иероглиф, результат случайного совпадения нескольких байтов со схемой кодирования UTF-8.
Думаю, вы уже понимаете и то, почему при переносе скомпилированного кода из-под Windows в Linux все перестало
работать. Байты, выводимые на консоль, соответствую Cp866
. А в Linux ожидается
. При переносе с Linux на Windows код тоже ломается, правда, по другой причине. При компиляции
UTF-8
там все в порядке, ибо кодировки файла и системы совпадают (если вы помните, там нормально работал именно
UTF-8
вариант кода). А вот при выводе на консоль используется системная кодировка, отличная от
консоли.
Итак, к какому выводу можно придти. Кодировку надо учитывать как во время компиляции, так и при
выводе на консоль. Невыполнение этого правила в любом из двух мест способно вызвать проблемы.
Как именно надо учитывать кодировку? Очень просто.
Как это должно быть сделано
Начнем с компиляции.
У компилятора есть опция -encoding
. Она позволяет указать кодировку исходника. Вот ее и надо
использовать. Попробуем на примере с шипящими. Под Windows:
javac -cp . -d . -encoding Cp866 HelloWorldCp866_2.java java -cp . test.HelloWorldCp866_2 ╠√╪ър ёє╪хъ эрёє╪шыр!
И под Linux:
$ javac -cp . -d . -encoding Cp866 HelloWorldCp866_2.java $ java -cp . test.HelloWorldCp866_2 МыШка суШек насуШила!
Если сравнивать с предыдущим вариантом, радует уже хотя бы отсутствие предупреждений
типа unmappable character for encoding XXX – исходник прочитан корректно. Под Linux и
выполнение замечательно – кодировка консоли совпадает с системной. А вот в Windows при выполнении
по-прежнему грустная картина. Не беда, сейчас и это поправим.
Итак, для того, чтобы обеспечить правильный вывод на консоль, нужно в приложении установить кодировку этой
консоли. Делается это просто. Тип поля System.out
– java.io.PrintStream
.
У этого класса есть один конструктор, в который можно передать кодировку. Этот конструктор и надо
использовать для создания нового потока с правильной кодировкой над существующим System.out
:
System.setOut(new java.io.PrintStream(System.out, true, "Cp866"));
И всё бы хорошо. Но в разных системах кодировки консоли разные. Потому – лучше выставить значение
кодировки через свойство виртуальной машины. При старте проверить его наличие, и если установлено –
использовать. Это средство будет универсальным. Т.е. код установки кодировки консоли получится такой:
String consoleEncoding = System.getProperty("consoleEncoding"); if (consoleEncoding != null) { try { System.setOut(new PrintStream(System.out, true, consoleEncoding)); } catch (java.io.UnsupportedEncodingException ex) { System.err.println("Unsupported encoding set for console: "+consoleEncoding); } }
Вот версия в кодировке Cp866
, в которой всё это учтено:
HelloWorldCp866_2_ok.java. Компилируем с ключом -encoding Cp866
, запускаем в различных
вариантах:
javac -cp . -d . -encoding Cp866 HelloWorldCp866_2_ok.java java -cp . test.HelloWorldCp866_2_ok ╠√╪ър ёє╪хъ эрёє╪шыр! java -cp . -DconsoleEncoding=C866 test.HelloWorldCp866_2_ok Unsupported encoding set for console: C866 ╠√╪ър ёє╪хъ эрёє╪шыр! java -cp . -DconsoleEncoding=Cp866 test.HelloWorldCp866_2_ok МыШка суШек насуШила!
Ура! Шипящие появились! А ну-ка попробуем изначальную версию в UTF-8, с добавлением кодировки консоли (HelloWorldUTF8_ok.java):
javac -cp . -d . -encoding UTF-8 HelloWorldUTF8_ok.java java -cp . test.HelloWorldUTF8_ok ╟фЁртёЄтєщ, ╠шЁ! java -cp . -DconsoleEncoding=Cp866 test.HelloWorldUTF8_ok Здравствуй, Мир!
И тут при указании кодировки консоли все в порядке. В Linux же все еще проще, там даже кодировку указывать
необязательно:
$ javac -cp . -d . -encoding Cp866 HelloWorldCp866_2_ok.java $ java -cp . test.HelloWorldCp866_2_ok МыШка суШек насуШила!
Ну и последняя проверка – переносимость. Вот написаные и скомпилированные уже по всем правилам
test.HelloWorldCp866_2_ok
и test.HelloWorldUTF8_ok
:
test_ok.zip. Компиляция сделана под Windows, впрочем, тут уже никакой разницы. Запускаем под Linux:
$ java -cp . test.HelloWorldCp866_2_ok МыШка суШек насуШила! $ java -cp . test.HelloWorldUTF8_ok Здравствуй, Мир!
При обратном переносе ситуация аналогичная, так что консольный вывод приводит не буду.
* * *
Вот мы и добрались до финиша. Что мы вынесли из прочитанного:
Для того, чтобы исключить проблемы с кодировками при компиляции и консольном выводе,
необходимо:
- В явном виде указать компилятору кодировку исходника – с помощью опции
-encoding
или же каким-либо другим способом (вant
иmaven
есть
свои настройки, в средах разработки также можно указать кодировку исходников) - В явном виде установить кодировку консоли с помощью вызова:
System.setOut(new java.io.PrintStream(System.out, true, "<имя кодировки>"));
Всё то же самое применимо и к потоку System.err
. С System.in
, использующимся для
чтения из консоли, ситуация немного другая, это чистый java.io.InputStream
, потому для учета
кодировки из System.in
надо читать, оборачивая его в java.io.Reader
(а точнее, в его
наследника) с указанием кодировки.
Вот теперь – совсем всё! Всем спасибо!
P.S. А для чего это?
Как-то за кадром остался один вопрос. А для чего всё это вообще надо? Я разрабатываю все под
Windows, исполняю там же. Зачем мне все эти пляски с бубном про кодировки? Один раз настроил – и
хорошо.
Так вот. Дело в современных тенденциях в разработке ПО – она становится все более и более
распределенной. И, что более важно, над одним проектом начинают работать люди, говорящие на разных языках,
работающие под разными ОС и с большой вероятностью использующие разные кодовые страницы. Так вот, чтобы не
иметь проблем с исходниками, написанными товарищем по команде – жизненно необходимо работать в одной
кодировке. Иначе коллега пишет комментарии на немецком – Cp1252 – а я не могу их прочитать,
часть символов у меня заменяется на русские буквы. Ну ладно, с немецким еще как-то, а когда комментарии на
иврите? На японском? Единая кодировка – например, UTF-8, – эту проблему решает.
А по хорошему, конечно, код вообще не должен содержать ничего, кроме нижних 127 символов
, т.е.
iso-8859-1ASCII
. Тогда будет гарантия чтения исходника в любых условиях.
Вот теперь действительно всё!
P.S. Обсуждение статьи: http://skipy-ru.livejournal.com/4755.html.
В начало