Почему декодирование stdout не работает при добавлении возврата каретки?

Следующий код Java делает именно то, что ожидается:

1      String s = "♪♬♪♪♬♪♪♬♪♪♬♪♪♬♪♪♬♪";
2      for(int i=0; i < s.length(); i++)
3      {
4         System.out.print(s.substring(i,i+1));
5         //System.out.print("\r");
6         Thread.currentThread().sleep(500);
7      }

Но когда я пытаюсь добавить возврат каретки, комментируя строку 5, он печатает ?s. Почему это и как мне это исправить?

(Я также пробовал с "\ u240d" для возврата каретки - то же самое).

РЕДАКТИРОВАТЬ: вывод идет на bash в Mac OS X.


person Kai Huppmann    schedule 20.11.2009    source источник
comment
Вы хотите, чтобы эти заметки выводились друг под другом или перезаписывались на месте в начале строки?   -  person Carl Smotricz    schedule 20.11.2009
comment
System.out.println не будет работать?   -  person Anthony Forloney    schedule 20.11.2009
comment
Только если он хочет распечатать вниз.   -  person Carl Smotricz    schedule 20.11.2009
comment
должны ли мы предположить, что он хочет отображать по одному символу за раз?   -  person Anthony Forloney    schedule 20.11.2009
comment
Может \u000d работает лучше? System.out.print('\u000d'); или System.out.print("\u000d");   -  person VonC    schedule 20.11.2009
comment
@Carl: Да, они должны перезаписывать друг друга, и @aforloney: Карл прав, поэтому println не будет работать.   -  person Kai Huppmann    schedule 20.11.2009
comment
Вау, U+240D уже имя SYMBOL для возврата каретки. Я бы не возлагал больших надежд на то, что это сработает :-). VonC: это было бы похоже на то, как если бы вы включили буквальный CR в процедуру. В прошлый раз, когда я смотрел, они недействительны в строковых или символьных литералах.   -  person Joey    schedule 20.11.2009
comment
@Carl и @Johannes: использование = (или =) хорошо работает при анализе путем чтения из файла свойств (System.out.print(props.get(UTF8240D))) и для обычного символа, например замена примечаний на ABCDEFGHIJKLMNOP, который также работает с \r.   -  person Kai Huppmann    schedule 20.11.2009


Ответы (3)


Java не знает, что ваш исходный файл имеет кодировку UTF-8.

Если вы скомпилируете с

javac -encoding utf8 MyClass.java

и беги с

java -Dfile.encoding=utf8 MyClass

это будет работать.

(Кто-нибудь знает, почему UTF-8 не используется по умолчанию?)

person Jason Orendorff    schedule 20.11.2009
comment
Спасибо также за другие ответы от dtsazza и sascha. Несмотря на то, что они были (в основном) правы и позволили запрограммировать обходной путь, Джейсон понимает, что предлагает простое решение без изменения кода. - person Kai Huppmann; 20.11.2009

пожалуйста, также напечатайте s.length (), я уверен, что это больше, чем 18. Представление строки Java - utf-16, String.substring просто извлекает значения char. музыкальные ноты начинаются с 0x1d000 - они не помещаются в один символ. чтобы извлечь полные кодовые точки/глифы из строки, используйте что-то вроде проект icu — UCharacterIterator

PS: я не знаю, может ли ваш сеанс терминала отображать эти символы вообще

person sascha    schedule 20.11.2009
comment
Предполагая, что символы, вставленные в Firefox, одинаковы в приложении, это U+266A и U+266C, оба в базовой многоязычной плоскости. - person McDowell; 20.11.2009

Я ожидаю, что это связано с тем, как ваш терминал интерпретирует вывод.

Как было указано выше, все глифы нот являются многобайтовыми символами. Кроме того, Java char имеет ширину всего 16 бит, поэтому один char не может надежно представлять один символ Unicode сам по себе - и, следовательно, метод String.substring не полностью поддерживает многобайтность.

Таким образом, скорее всего, при каждой итерации цикла Java выводит как бы полсимвола. Когда печатается первый байт пары, терминал понимает, что это первая половина многобайтового символа, и не отображает его. Когда печатается следующий байт, терминал видит полный символ, соответствующий ноте, и отображает его.

Что происходит, когда вы раскомментируете println("\r"), так это то, что вы вставляете новую строку в середине двух половинок каждого символа. Таким образом, терминал никогда не получает последовательность байтов, например. 0x26, 0x6C, представляющий примечание, но вместо этого получает 0x26, 0x10 , 0x6C, 0x10 поэтому заметка не отображается.

person Andrzej Doyle    schedule 20.11.2009
comment
Так что, это. Думаю, я только что вспомнил, что он уже, чем int, будет содержать символ ASCII, но не много экзотических символов Unicode, а затем просто не подумал/проверил. Спасибо за несколько неловкую поправку! - person Andrzej Doyle; 20.11.2009