50 votes

Pourquoi Java permettre les caractères de contrôle dans ses identifiants?

Le Mystère

En explorant précisément les caractères qui ont été autorisés dans Java identifiants, j'ai trébuché sur quelque chose de très curieux qu'il semble presque certain d'être un bug.

Je m'attendais à trouver que Java identificateurs conformés à l'obligation de commencer avec des personnages qui ont la propriété Unicode ID_Start et sont suivis par ceux qui ont la propriété ID_Continue, avec une exception accordée pour les principaux traits de soulignement et des signes dollar. Qui ne s'avère pas être le cas, et ce que j'ai trouvé est à l'extrême de la variance ou de toute autre idée d'un identifiant que j'ai entendu parler de.

Courte Démo

Considérons l'exemple suivant prouve qu'un caractère ASCII ESC (octal 033) est autorisé en Java identifiants:

$ perl -le 'print qq(public class escape { public static void main(String argv[]) { String var_\033 = "i am escape: \033"; System.out.println(var_\033); }})' > escape.java
$ javac escape.java
$ java escape | cat -v
i am escape: ^[

C'est même pire que cela, cependant. Presque infiniment pire, en fait. Même les valeurs Null sont autorisées! Et des milliers d'autres points de code qui ne sont même pas identificateur de caractères. J'ai testé cela sur Solaris, Linux et Mac exécution de Darwin, et tous donnent les mêmes résultats.

Longue Démo

Voici un programme de test qui va vous montrer tous ces imprévus points de code Java assez outrageuosly permet dans le cadre d'un identificateur de nom.

#!/usr/bin/env perl
# 
# test-java-idchars - find which bogus code points Java allows in its identifiers
# 
#   usage: test-java-idchars [low high]
#   e.g.:  test-java-idchars 0 255
#
# Without arguments, tests Unicode code points
# from 0 .. 0x1000.  You may go further with a
# higher explicit argument.
#
# Produces a report at the end.
#
# You can ^C it prematurely to end the program then
# and get a report of its progress up to that point.
#
# Tom Christiansen
# tchrist@perl.com
# Sat Jan 29 10:41:09 MST 2011

use strict;
use warnings;

use encoding "Latin1";
use open IO => ":utf8";

use charnames ();

$| = 1;

my @legal;

my ($start, $stop) = (0, 0x1000);

if (@ARGV != 0) {
    if (@ARGV == 1) {
        for (($stop) = @ARGV) { 
            $_ = oct if /^0/;   # support 0OCTAL, 0xHEX, 0bBINARY
        }
    }
    elsif (@ARGV == 2) {
        for (($start, $stop) = @ARGV) { 
            $_ = oct if /^0/;
        }
    } 
    else {
        die "usage: $0 [ [start] stop ]\n";
    } 
} 

for my $cp ( $start .. $stop ) {
    my $char = chr($cp);

    next if $char =~ /[\s\w]/;

    my $type = "?";
    for ($char) {
        $type = "Letter"      if /\pL/;
        $type = "Mark"        if /\pM/;
        $type = "Number"      if /\pN/;
        $type = "Punctuation" if /\pP/;
        $type = "Symbol"      if /\pS/;
        $type = "Separator"   if /\pZ/;
        $type = "Control"     if /\pC/;
    } 
    my $name = $cp ? (charnames::viacode($cp) || "<missing>") : "NULL";
    next if $name eq "<missing>" && $cp > 0xFF;
    my $msg = sprintf("U+%04X %s", $cp, $name);
    print "testing \\p{$type} $msg...";
    open(TESTPROGRAM, ">:utf8", "testchar.java") || die $!;

print TESTPROGRAM <<"End_of_Java_Program";

public class testchar { 
    public static void main(String argv[]) { 
        String var_$char = "variable name ends in $msg";
        System.out.println(var_$char); 
    }
}

End_of_Java_Program

    close(TESTPROGRAM) || die $!;

    system q{
        ( javac -encoding UTF-8 testchar.java \
            && \
          java -Dfile.encoding=UTF-8 testchar | grep variable \
        ) >/dev/null 2>&1
    };

    push @legal, sprintf("U+%04X", $cp) if $? == 0;

    if ($? && $? < 128) {
        print "<interrupted>\n";
        exit;  # from a ^C
    } 

    printf "is %s in Java identifiers.\n",  
        ($? == 0) ? uc "legal" : "forbidden";

} 

END { 
    print "Legal but evil code points: @legal\n";
}

Voici un exemple d'exécution de ce programme sur la première 33 points de code qui ne sont ni espaces, ni caractères identifiant:

$ perl test-java-idchars 0 0x20
testing \p{Control} U+0000 NULL...is LEGAL in Java identifiers.
testing \p{Control} U+0001 START OF HEADING...is LEGAL in Java identifiers.
testing \p{Control} U+0002 START OF TEXT...is LEGAL in Java identifiers.
testing \p{Control} U+0003 END OF TEXT...is LEGAL in Java identifiers.
testing \p{Control} U+0004 END OF TRANSMISSION...is LEGAL in Java identifiers.
testing \p{Control} U+0005 ENQUIRY...is LEGAL in Java identifiers.
testing \p{Control} U+0006 ACKNOWLEDGE...is LEGAL in Java identifiers.
testing \p{Control} U+0007 BELL...is LEGAL in Java identifiers.
testing \p{Control} U+0008 BACKSPACE...is LEGAL in Java identifiers.
testing \p{Control} U+000B LINE TABULATION...is forbidden in Java identifiers.
testing \p{Control} U+000E SHIFT OUT...is LEGAL in Java identifiers.
testing \p{Control} U+000F SHIFT IN...is LEGAL in Java identifiers.
testing \p{Control} U+0010 DATA LINK ESCAPE...is LEGAL in Java identifiers.
testing \p{Control} U+0011 DEVICE CONTROL ONE...is LEGAL in Java identifiers.
testing \p{Control} U+0012 DEVICE CONTROL TWO...is LEGAL in Java identifiers.
testing \p{Control} U+0013 DEVICE CONTROL THREE...is LEGAL in Java identifiers.
testing \p{Control} U+0014 DEVICE CONTROL FOUR...is LEGAL in Java identifiers.
testing \p{Control} U+0015 NEGATIVE ACKNOWLEDGE...is LEGAL in Java identifiers.
testing \p{Control} U+0016 SYNCHRONOUS IDLE...is LEGAL in Java identifiers.
testing \p{Control} U+0017 END OF TRANSMISSION BLOCK...is LEGAL in Java identifiers.
testing \p{Control} U+0018 CANCEL...is LEGAL in Java identifiers.
testing \p{Control} U+0019 END OF MEDIUM...is LEGAL in Java identifiers.
testing \p{Control} U+001A SUBSTITUTE...is LEGAL in Java identifiers.
testing \p{Control} U+001B ESCAPE...is LEGAL in Java identifiers.
testing \p{Control} U+001C INFORMATION SEPARATOR FOUR...is forbidden in Java identifiers.
testing \p{Control} U+001D INFORMATION SEPARATOR THREE...is forbidden in Java identifiers.
testing \p{Control} U+001E INFORMATION SEPARATOR TWO...is forbidden in Java identifiers.
testing \p{Control} U+001F INFORMATION SEPARATOR ONE...is forbidden in Java identifiers.
Legal but evil code points: U+0000 U+0001 U+0002 U+0003 U+0004 U+0005 U+0006 U+0007 U+0008 U+000E U+000F U+0010 U+0011 U+0012 U+0013 U+0014 U+0015 U+0016 U+0017 U+0018 U+0019 U+001A U+001B

Et voici une autre démonstration:

$ perl test-java-idchars 0x600 0x700 | grep -i legal
testing \p{Control} U+0600 ARABIC NUMBER SIGN...is LEGAL in Java identifiers.
testing \p{Control} U+0601 ARABIC SIGN SANAH...is LEGAL in Java identifiers.
testing \p{Control} U+0602 ARABIC FOOTNOTE MARKER...is LEGAL in Java identifiers.
testing \p{Control} U+0603 ARABIC SIGN SAFHA...is LEGAL in Java identifiers.
testing \p{Control} U+06DD ARABIC END OF AYAH...is LEGAL in Java identifiers.
Legal but evil code points: U+0600 U+0601 U+0602 U+0603 U+06DD

La Question

Quelqu'un peut-il m'expliquer ce apparemment fous comportement? Il y a beaucoup, beaucoup, beaucoup d'autres inexplicablement permis points de code tous sur la place, à commencer tout de suite avec U+0000, qui est peut-être le plus étrange de tous. Si vous l'exécutez sur la première 0x1000 points de code, vous voyez certains modèles apparaissent, comme l'autorisation de tout et de tous les points de code de la propriété Current_Symbol. Mais trop de choses est totalement inexplicable, au moins par moi.

15voto

ninjalj Points 22026

La Java Language Specification section 3.8 reporte à Caractère.isJavaIdentifierStart() et de Caractère.isJavaIdentifierPart(). Ce dernier, parmi les autres conditions de Caractère.isIdentifierIgnorable(), qui permet à des non-blanc des caractères de contrôle (y compris l'ensemble de la gamme C1, voir le lien pour la liste).

8voto

Avi Points 14468

Une autre question pourrait être: Pourquoi ne pas Java permettre les caractères de contrôle dans ses identifiants?

Un bon principe lors de la conception d'une langue ou d'un autre système, est de ne pas interdire quoi que ce soit sans une bonne raison, puisque vous ne savez jamais comment il peut être utilisé, et le moins de règles maîtres d'œuvre et les utilisateurs ont à composer avec, mieux c'est.

Il est vrai que vous avez certainement ne devriez pas prendre avantage de cela, en fait, l'intégration s'échappe dans vos noms de variable, et vous ne verrez pas toutes les bibliothèques populaires qui exposent les classes avec des caractères nuls dans leur.

Certes, cela pourrait être abusé, mais ce n'est pas la langue concepteurs de travail pour protéger les programmeurs d'eux-mêmes de cette façon, pas plus qu'en forçant la bonne indentation ou bien choisi les noms de variables.

-1voto

mihi Points 3476

Vous pouvez utiliser l'unicode s'échappe dans le code pour désigner les variables, j'. e.

int a\u0000 = 9;

est valable de code Java. De cette façon, vous n'avez pas besoin de les "méchants" dans le code source.

(Vous pouvez utiliser l'Unicode échappe partout ailleurs, aussi, par exemple pour l'espace ou à l'intérieur même des mots-clés... Qui peut être source de confusion, comme \u0022 sera la fin d'une chaîne de caractères, mais je suppose que c'est juste que le Java de créateurs ont décidé de garder sa cohérence.)

-2voto

irreputable Points 25577

Je ne vois pas quel est le big deal. Comment affectent-vous de toute façon?

Si un développeur veut dissimuler son code, il peut le faire avec l'ASCII.

Si un développeur veut faire son code compréhensible, il va utiliser la lingua franca de l'industrie: de l'anglais. Non seulement les identifiants sont ASCII seulement, mais aussi de communes des mots anglais. Sinon, personne ne pourra l'utiliser ou lire son code, il peut utiliser toutes les personnages qu'il aime.

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