2013-12-16 1 views
1

У меня возникла своеобразная проблема. Мой сервлет получает строку с urlencoded, и из журнала я могу сказать, что эта строка верна.java.net.URLDecoder зависит от кодировки исходного файла?

Я попытался с этой строкой:

"test+%F0%9F%98%8E+1+%E2%99%A7+%E2%99%A2+%E2%99%A1+%E2%99%A4+%E3%80%8A" 

, который является следующим:

"test 1 ♧ ♢ ♡ ♤ 《" 

Однако, когда я запускаю тест, я получаю тот же результат, как я получаю на моем сервере:

"test ? 1 ? ? ? ? ?" 

Сброс шестнадцатеричных кодов Я получаю

00: 74 65 73 74 20 3F 20 31 20 3F 20 3F 20 3F 20 3F | test ? 1 ? ? ? ? 
10: 20 3F -- -- -- -- -- -- -- -- -- -- -- -- -- -- | ?     

Где я ожидал:

00: 74 65 73 74 20 F0 9F 98 8E 20 31 20 E2 99 A7 20 | test ... . 1 ... 
10: E2 99 A2 20 E2 99 A1 20 E2 99 A4 20 E3 80 8A -- | ... ... ... ... 

Теперь для "интересной" бит. Это происходит на моем сервере и в моей Eclipse IDE, но если я затем сохраню исходный файл в UTF-8, URLDecoder вернет правильные данные! Это не помогло на моем сервере.

1: Я не вижу, как это может быть так, URLDecoder должен прослушивать запрошенную кодировку. 2: Мне явно нужна замена для java.net.URLDecoder, если он это делает, он принципиально нарушен. Какие-либо предложения?

Код испытания:

public class URLDecoderTest { 
    public static void main(String[] args) { 
     String reqMsg = "test+%F0%9F%98%8E+1+%E2%99%A7+%E2%99%A2+%E2%99%A1+%E2%99%A4+%E3%80%8A"; 
     System.out.println("reqMsg  : " + reqMsg); 
     try { 
      reqMsg = URLDecoder.decode(reqMsg, "UTF-8"); 
     } catch (UnsupportedEncodingException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     System.out.println("reqMsg  : " + reqMsg); 
     System.out.println(HexTools.dump(reqMsg)); 
     System.out.println("Expected (fixed):"); 
     System.out.println("00: 74 65 73 74 20 F0 9F 98 8E 20 31 20 E2 99 A7 20 | test ... . 1 ... "); 
     System.out.println("10: E2 99 A2 20 E2 99 A1 20 E2 99 A4 20 E3 80 8A -- | ... ... ... ..."); 
    } 
} 

Примечание: HexTools от Mobicents: http://code.google.com/p/mobicents/source/browse/trunk/commons/src/main/java/org/mobicents/commons/HexTools.java?r=21908

Edit: Глядя на источник URLDecoder.decode, он использует новый String (байт, 0, pos, enc) для декодирования байтов. По какой-то причине это не удается, но для unicode новая строка (bytes, 0, pos) работает нормально.

Есть ли ошибка в классе StringCoding Java, что она автоматически возвращается к кодировке по умолчанию, независимо от того, что ей передается? метод декодирования, вызываемый String, является статическим, и он устанавливает запрошенную кодировку другим статическим методом перед вызовом декодирования, который затем будет использовать этот статический. Другими словами: это не потокобезопасный !!!

Обновление: У меня были проблемы практически во всех слоях моих реализаций. Символ Emoji (4-байтовые символы utf-8) вызвал, например, проблемы с MySQL. Я получил от него asciified символы, даже если он был установлен в utf8.

Заключительное замечание: Часть проблемы или предполагаемой проблемы действительно, было вызвано злоупотреблением HexTools.dump (String), класс построен для обработки двоичных данных, где символы даже струны только содержащиеся на нем данные в низких байт.

Для справок в будущем обращайтесь к HexTools.свалка должна быть:

 System.out.println(HexTools.dump(reqMsg.getBytes("UTF-8"))); 

с фиксирующим блоком для UnsupportedEncodingException съехал, чтобы покрыть эту линию курса. Выполнение этого, возвращает шестнадцатеричный кадр, идентичный ожидаемому.

+0

Где-то вдоль линии ваши данные преобразуются в ASCII. –

+0

Единственное место, которое может произойти, находится внутри java.net.URLDecoder, оно не должно. –

+0

Мне не хватает опыта работы с java.net, чтобы помочь вам больше, но это похоже на поведение преобразования utf8 в ASCII. –

ответ

2

Этот код работает, как ожидалось:

import java.io.IOException; 
import java.net.URLDecoder; 

public class Dump { 
    public static void main(String[] args) throws IOException { 
    String reqMsg = 
     "test+%F0%9F%98%8E+1+%E2%99%A7+%E2%99%A2+%E2%99%A1+%E2%99%A4+%E3%80%8A"; 
    String decoded = URLDecoder.decode(reqMsg, "UTF-8"); 
    // UTF-16 
    for (char ch : decoded.toCharArray()) { 
     System.out.format("%04x ", (int) ch); 
    } 
    System.out.println(); 
    // UTF-8 
    for (byte ch : decoded.getBytes("UTF-8")) { 
     System.out.format("%02x ", 0xFF & ch); 
    } 
    } 
} 

Однако, вы можете потерять информацию здесь:

System.out.println 

выше PrintStream будет выполнять (потенциально с потерями) операции перекодировки. Из документации:

Все символы, напечатанные PrintStream, преобразуются в байты, используя кодировку символов по умолчанию для платформы.

Во многих системах Java использует устаревшую кодировку.

Возможно, ваш контейнер сервлетов неправильно сконфигурирован. Не уверен, что это верно для последних версий, но Tomcat исторически дефолт по ISO-8859-1 для кодирования URL.

+0

Кроме того, если я даю println правильно декодированную строку, она печатает ее правильно. Как и HexTools, кстати. –

+0

Этот код HexTools не реализован хорошо. Он использует потенциально lossy 'data.getBytes()' для преобразования строки в байты. Такая же строка может привести к тому, что различные байты будут напечатаны на разных системах. – McDowell

+0

HexTools не были написаны для этого, я думаю. Он предназначен для сброса двоичных данных, а метод со строковым вводом - это данные только в первом байте символов в результирующем массиве. –

2

HexTools.dump должен ошибаться. Он передается String = текст Юникода. Итак, как он может сбрасывать байты? Помимо использования кодировки платформы по умолчанию, возможно, Windows ANSI.

Try что-то вроде:

System.out.println(Arrays.toString(reqMsg.getBytes(StandardCharsets.UTF_8))); 

Вы не увидите знак вопроса (0x3F == 63).

+0

Затем System.out.println ("reqMsg:" + reqMsg); терпит неудачу. Декодированная строка ошибочна, HexTools записывается для сброса двоичных данных. Возможно, вы правы в том, что здесь печатаются неверные данные, однако println показывает одну и ту же ошибку декодирования. –

+0

Да, System.out использует тот же самый по умолчанию, платформенный код. –

+1

Я нашел источник HexTools с перегруженным 'dump (byte [])'. Как сказано, 'dump (String)' явно глупо. Их использование StringBuffer вместо более подходящего StringBuilder намекает на то, что он не использует этот класс. Хотя я нахожу это освежающим, когда люди демонстрируют умность, не изобретая колесо. –