27 votes

Comment déterminer si une chaîne contient des caractères codés non valides

Scénario d'utilisation

Nous avons mis en place un webservice que notre interface web les développeurs à utiliser (via une api php) en interne pour afficher des données de produit. Sur le site web de l'utilisateur tape quelque chose (c'est à dire une chaîne de requête). En interne, le site web fait un appel au service via l'api.

Remarque: Nous utilisons restlet, pas de tomcat

Problème D'Origine

Firefox 3.0.10 semble respecter l'encodage sélectionné dans le navigateur et coder une url en fonction de l'encodage. Cela ne se traduisent dans les différentes chaînes de requête pour ISO-8859-1 et UTF-8.

Notre site web transmet l'entrée de l'utilisateur et de ne pas le convertir (ce qui doit être), donc il peut faire appel au service via l'api d'appeler un service web à l'aide d'une chaîne de requête qui contient des accents.

I. e. pour une requête de la partie la recherche comme

    ...v=abcädef

si "ISO-8859-1" est sélectionné, l'envoi de la requête de la partie ressemble

...v=abc%E4def

mais si "UTF-8" est sélectionné, l'envoi de la requête de la partie ressemble

...v=abc%C3%A4def

Solution Souhaitée

Comme nous le contrôle du service, car nous avons mis en œuvre, nous voulons vérifier sur le côté serveur si l'appel ne contient pas de caractères utf-8, si oui, répondre avec un 4xx d'état http

Solution Actuelle En Détail

Vérifier pour chaque caractère ( == chaîne de caractères.substring(i,i+1) )

  1. si le personnage.getBytes()[0] est égal à 63 ans pour les '?'
  2. si le Personnage.getType(caractère.charAt(0)) renvoie OTHER_SYMBOL

Code

protected List< String > getNonUnicodeCharacters( String s ) {
  final List< String > result = new ArrayList< String >();
  for ( int i = 0 , n = s.length() ; i < n ; i++ ) {
    final String character = s.substring( i , i + 1 );
    final boolean isOtherSymbol = 
      ( int ) Character.OTHER_SYMBOL
       == Character.getType( character.charAt( 0 ) );
    final boolean isNonUnicode = isOtherSymbol 
      && character.getBytes()[ 0 ] == ( byte ) 63;
    if ( isNonUnicode )
      result.add( character );
  }
  return result;
}

Question

Sera-ce à attraper tous invalide (non encodé en utf) caractères? Ne vous avez un mieux (plus facile) solution?

Note: j'ai vérifié URLDecoder avec le code suivant

final String[] test = new String[]{
  "v=abc%E4def",
  "v=abc%C3%A4def"
};
for ( int i = 0 , n = test.length ; i < n ; i++ ) {
    System.out.println( java.net.URLDecoder.decode(test[i],"UTF-8") );
    System.out.println( java.net.URLDecoder.decode(test[i],"ISO-8859-1") );
}

Cette affiche:

v=abc?def
v=abcädef
v=abcädef
v=abcädef

et il n'est pas à jeter un IllegalArgumentException soupir

26voto

ZZ Coder Points 36990

J'ai posé la même question,

http://stackoverflow.com/questions/1233076/handling-character-encoding-in-uri-on-tomcat

J'ai récemment trouvé une solution et cela fonctionne assez bien pour moi. Vous pourriez vouloir essayer. Voici ce que vous devez faire,

  1. Laissez votre URI de l'encodage Latin-1. Sur Tomcat, ajouter URIEncoding="ISO-8859-1" pour le Connecteur server.xml.
  2. Si vous avez manuellement l'URL de décoder, de l'utilisation Latin1 comme jeu.
  3. Utiliser le fixEncoding() de la fonction de correction des encodages.

Par exemple, pour obtenir un paramètre de chaîne de requête,

  String name = fixEncoding(request.getParameter("name"));

Vous pouvez le faire toujours. Chaîne avec encodage correct n'est pas modifié.

Le code est ci-joint. Bonne chance!

 public static String fixEncoding(String latin1) {
  try {
   byte[] bytes = latin1.getBytes("ISO-8859-1");
   if (!validUTF8(bytes))
    return latin1;   
   return new String(bytes, "UTF-8");  
  } catch (UnsupportedEncodingException e) {
   // Impossible, throw unchecked
   throw new IllegalStateException("No Latin1 or UTF-8: " + e.getMessage());
  }

 }

 public static boolean validUTF8(byte[] input) {
  int i = 0;
  // Check for BOM
  if (input.length >= 3 && (input[0] & 0xFF) == 0xEF
    && (input[1] & 0xFF) == 0xBB & (input[2] & 0xFF) == 0xBF) {
   i = 3;
  }

  int end;
  for (int j = input.length; i < j; ++i) {
   int octet = input[i];
   if ((octet & 0x80) == 0) {
    continue; // ASCII
   }

   // Check for UTF-8 leading byte
   if ((octet & 0xE0) == 0xC0) {
    end = i + 1;
   } else if ((octet & 0xF0) == 0xE0) {
    end = i + 2;
   } else if ((octet & 0xF8) == 0xF0) {
    end = i + 3;
   } else {
    // Java only supports BMP so 3 is max
    return false;
   }

   while (i < end) {
    i++;
    octet = input[i];
    if ((octet & 0xC0) != 0x80) {
     // Not a valid trailing byte
     return false;
    }
   }
  }
  return true;
 }

EDIT: Votre approche ne fonctionne pas pour diverses raisons. Quand il y a des erreurs de codage, vous ne pouvez pas compter sur ce que vous obtenez à partir de Tomcat. Parfois, vous obtenez � ou ?. D'autres fois, vous ne seriez pas obtenir quoi que ce soit, getParameter() renvoie la valeur null. Dire que vous pouvez les vérifier "?", qu'advient-il de votre chaîne de requête contient valide "?" ?

En outre, vous ne devriez pas refuser la demande. Ce n'est pas votre faute de l'utilisateur. Comme je l'ai mentionné dans ma question initiale, le navigateur peut encoder l'URL soit en UTF-8 ou en Latin-1. L'utilisateur n'a aucun contrôle. Vous devez accepter les deux. La modification de votre servlet pour le Latin-1 vous permettra de préserver tous les personnages, même s'ils sont mauvais, pour nous donner une chance de le corriger ou de le jeter.

La solution que j'ai posté ici n'est pas parfait, mais c'est la meilleure que nous avons trouvé jusqu'à présent.

9voto

ante Points 614

Vous pouvez utiliser un CharsetDecoder configuré pour lever une exception si des caractères non valides sont trouvés:

  CharsetDecoder UTF8Decoder =
      Charset.forName("UTF8").newDecoder().onMalformedInput(CodingErrorAction.REPORT);
 

Voir CodingErrorAction.REPORT

3voto

Brian Agnew Points 143181

URLDecoder décoder pour un codage donnée. Cela devrait marquer les erreurs de façon appropriée. Cependant, les états de documentation:

Il y a deux façons possibles de ce décodeur peut traiter illégale des chaînes de caractères. Il pourrait laisser des caractères illégaux seul, ou qu'il pourrait jeter une IllegalArgumentException. Qui approche le décodeur prend est laissé à la mise en œuvre.

De sorte que vous devriez essayer. Notez également (à partir de la decode() documentation de la méthode):

Le World Wide Web Consortium Recommandation stipule que l'UTF-8 doit être utilisé. Ne pas le faire peut introduire des incompatibilites

donc, il y a autre chose à penser!

EDIT: Apache Commons URLDecode prétend jeter des exceptions appropriées pour mauvais encodages.

3voto

Adrian McCarthy Points 17018

J'ai travaillé sur un même "suppose que le codage" problème. La meilleure solution consiste à connaître l'encodage. Sauf que, vous pouvez faire des suppositions éclairées à la distinction entre UTF-8 et ISO-8859-1.

Pour répondre à la question générale de savoir comment détecter si une chaîne est correctement encodé en UTF-8, vous pouvez vérifier les choses suivantes:

  1. Aucun octet 0x00, 0xC0, 0xC1, ou dans la gamme 0xF5-0xFF.
  2. Queue octets (0x80-0xBF) sont toujours précédées par un chef d'octets 0xC2-0xF4 ou un autre octet queue.
  3. Tête d'octets doit prévoir correctement le nombre de queue octets (par exemple, n'importe quel octet dans 0xC2-0xDF devrait être suivie par exactement un octet dans la gamme 0x80-0xBF).

Si une chaîne de passe tous ces tests, il est alors interprétable comme UTF-8 valide. Cela ne garantira pas que c' est de l'UTF-8, mais c'est un bon prédicteur.

Juridique d'entrée en ISO-8859-1 n'aura probablement pas les caractères de contrôle (0x00-0x1F et 0x80-0x9F) autres que la ligne de séparation. Ressemble à 0x7F n'est pas définie dans la norme ISO-8859-1.

(Je suis en se fondant hors des pages de Wikipédia pour l'UTF-8 et ISO-8859-1.)

2voto

mfx Points 4517

Vous voudrez peut-être inclure un paramètre connu dans vos demandes, par exemple "... & encTest = ä €", pour différencier en toute sécurité entre les différents encodages.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X