Équivalent Sprintf en Java

284

Printf a été ajouté à Java avec la version 1.5, mais je n'arrive pas à trouver comment envoyer la sortie à une chaîne plutôt qu'à un fichier (ce que fait sprintf en C). Est-ce que quelqu'un sait comment faire ça?

cheval de papier
la source

Réponses:

474
// Store the formatted string in 'result'
String result = String.format("%4d", i * j);

// Write the result to standard output
System.out.println( result );

Voir le format et sa syntaxe

Eugene Yokota
la source
28

Les chaînes sont des types immuables. Vous ne pouvez pas les modifier, renvoyez uniquement de nouvelles instances de chaîne.

Pour cette raison, le formatage avec une méthode d'instance n'a pas de sens, car il faudrait l'appeler comme:

String formatted = "%s: %s".format(key, value);

Les auteurs Java d'origine (et les auteurs .NET) ont décidé qu'une méthode statique avait plus de sens dans cette situation, car vous ne modifiez pas la cible, mais appelez plutôt une méthode de formatage et passez une chaîne d'entrée.

Voici un exemple de pourquoi format()serait stupide comme méthode d'instance. En .NET (et probablement en Java), Replace()est une méthode d'instance.

Tu peux le faire:

 "I Like Wine".Replace("Wine","Beer");

Cependant, rien ne se passe, car les chaînes sont immuables. Replace()essaie de renvoyer une nouvelle chaîne, mais elle n'est affectée à rien.

Cela provoque de nombreuses erreurs de recrue courantes comme:

inputText.Replace(" ", "%20");

Encore une fois, rien ne se passe, vous devez plutôt faire:

inputText = inputText.Replace(" ","%20");

Maintenant, si vous comprenez que les chaînes sont immuables, cela est parfaitement logique. Si vous ne le faites pas, alors vous êtes simplement confus. L'endroit approprié Replace()serait où format()est, en tant que méthode statique de String:

 inputText = String.Replace(inputText, " ", "%20");

Maintenant, il ne fait aucun doute de ce qui se passe.

La vraie question est, pourquoi les auteurs de ces frameworks ont-ils décidé que l'une devait être une méthode d'instance et l'autre statique? À mon avis, les deux sont plus élégamment exprimés en tant que méthodes statiques.

Quelle que soit votre opinion, la vérité est que vous êtes moins enclin à faire une erreur en utilisant la version statique, et le code est plus facile à comprendre (No Hidden Gotchas).

Bien sûr, certaines méthodes sont parfaites comme méthodes d'instance, prenez String.Length ()

int length = "123".Length();

Dans cette situation, il est évident que nous n'essayons pas de modifier "123", nous l'inspectons simplement et retournons sa longueur. C'est un candidat parfait pour une méthode d'instance.

Mes règles simples pour les méthodes d'instance sur les objets immuables:

  • Si vous devez renvoyer une nouvelle instance du même type, utilisez une méthode statique.
  • Sinon, utilisez une méthode d'instance.
FlySwat
la source
4
Je vois que vous avez en quelque sorte eu l'idée que je suggérais que la chaîne de format devait être modifiée. Je n'ai jamais envisagé la possibilité que quelqu'un s'attende à ce qu'une chaîne change, car leur immuabilité est si fondamentale.
erickson
4
Étant donné que la chaîne de formatage ressemble généralement davantage à "Le prix est% 4d" et non à "% 4d", je vois encore beaucoup de risques de confusion. Qu'avez-vous contre les méthodes statiques? :)
FlySwat
44
Cette réponse semble n'avoir rien à voir avec la question.
Steve McLeod
2
La réponse n'est même pas Java, semble être plus pertinente pour .NET
Photodeus
3
-1. Vers le bas b / c c'est tangentiel. Et pas forcément le meilleur style pour les immuables. Ce style est plus détaillé que les appels de méthode simples, en particulier pour les opérations chaînées. Et il abandonne le polymorphisme d'exécution car il appelle des méthodes statiques, ce qui est significatif. YMMV.
Andrew Janke
3

Les deux solutions fonctionnent pour simuler printf, mais d'une manière différente. Par exemple, pour convertir une valeur en chaîne hexadécimale, vous avez les 2 solutions suivantes:

  • avec format(), le plus proche de sprintf():

    final static String HexChars = "0123456789abcdef";
    
    public static String getHexQuad(long v) {
        String ret;
        if(v > 0xffff) ret = getHexQuad(v >> 16); else ret = "";
        ret += String.format("%c%c%c%c",
            HexChars.charAt((int) ((v >> 12) & 0x0f)),
            HexChars.charAt((int) ((v >>  8) & 0x0f)),
            HexChars.charAt((int) ((v >>  4) & 0x0f)),
            HexChars.charAt((int) ( v        & 0x0f)));
        return ret;
    }
    
  • avec replace(char oldchar , char newchar), un peu plus rapide mais assez limité:

        ...
        ret += "ABCD".
            replace('A', HexChars.charAt((int) ((v >> 12) & 0x0f))).
            replace('B', HexChars.charAt((int) ((v >>  8) & 0x0f))).
            replace('C', HexChars.charAt((int) ((v >>  4) & 0x0f))).
            replace('D', HexChars.charAt((int) ( v        & 0x0f)));
        ...
    
  • Il existe une troisième solution consistant à simplement ajouter le caractère retun par un (les caractères sont des nombres qui s'ajoutent les uns aux autres !) Comme dans:

    ...
    ret += HexChars.charAt((int) ((v >> 12) & 0x0f)));
    ret += HexChars.charAt((int) ((v >>  8) & 0x0f)));
    ...
    

... mais ce serait vraiment moche.

догонят
la source
Toutes de bonnes idées, mais transforme votre code en code en écriture seule impossible à comprendre pour votre collègue.
Ben
0

Vous pouvez faire un printf sur tout ce qui est un OutputStream avec un PrintStream. En quelque sorte comme ça, en imprimant dans un flux de chaînes:

PrintStream ps = new PrintStream(baos);
ps.printf("there is a %s from %d %s", "hello", 3, "friends");
System.out.println(baos.toString());
baos.reset(); //need reset to write new string
ps.printf("there is a %s from %d %s", "flip", 5, "haters");
System.out.println(baos.toString());
baos.reset();

Le flux de chaînes peut être créé comme ceci ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
armagedescu
la source