Sprintf equivalente in Java


284

Printf è stato aggiunto a Java con la versione 1.5 ma non riesco a trovare come inviare l'output a una stringa anziché a un file (che è ciò che fa Sprintf in C). Qualcuno sa come fare questo?

Risposte:


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

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

Vedi il formato e la sua sintassi


28

Le stringhe sono tipi immutabili. Non è possibile modificarli, restituire solo nuove istanze di stringa.

Per questo motivo, la formattazione con un metodo di istanza ha poco senso, poiché dovrebbe essere chiamata come:

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

Gli autori Java originali (e gli autori .NET) hanno deciso che un metodo statico aveva più senso in questa situazione, poiché non si modifica la destinazione, ma si chiama un metodo di formattazione e si passa una stringa di input.

Ecco un esempio del perché format()sarebbe stupido come metodo di istanza. In .NET (e probabilmente in Java), Replace()è un metodo di istanza.

Puoi farlo:

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

Tuttavia, non succede nulla, perché le stringhe sono immutabili. Replace()tenta di restituire una nuova stringa, ma non è assegnata a nulla.

Ciò provoca molti errori rookie comuni come:

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

Ancora una volta, non succede nulla, invece devi fare:

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

Ora, se capisci che le stringhe sono immutabili, ha perfettamente senso. Se non lo fai, allora sei solo confuso. Il posto giusto per Replace()sarebbe dove si format()trova, come metodo statico di String:

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

Ora non ci sono dubbi su cosa stia succedendo.

La vera domanda è: perché gli autori di questi framework hanno deciso che uno dovrebbe essere un metodo di istanza e l'altro statico? A mio avviso, entrambi sono più elegantemente espressi come metodi statici.

Indipendentemente dalla tua opinione, la verità è che sei meno incline a fare un errore usando la versione statica, e il codice è più facile da capire (No Hidden Gotchas).

Naturalmente ci sono alcuni metodi che sono perfetti come metodi di istanza, prendi String.Length ()

int length = "123".Length();

In questa situazione, è ovvio che non stiamo cercando di modificare "123", stiamo solo controllando e restituendo la sua lunghezza. Questo è un candidato perfetto per un metodo di istanza.

Le mie semplici regole per i metodi di istanza su oggetti immutabili:

  • Se è necessario restituire una nuova istanza dello stesso tipo, utilizzare un metodo statico.
  • Altrimenti, utilizzare un metodo di istanza.

4
Vedo che in qualche modo hai avuto l'idea che stavo suggerendo di modificare la stringa di formato. Non ho mai considerato la possibilità che qualcuno possa aspettarsi che una stringa cambi, poiché la loro immutabilità è così fondamentale.
Erickson,

4
Visto che la stringa di formato è in genere più simile a "Il prezzo è% 4d" e non "% 4d", vedo ancora un sacco di potenziale confusione. Cosa hai contro i metodi statici? :)
FlySwat,

44
Questa risposta sembra non avere nulla a che fare con la domanda.
Steve McLeod,

2
La risposta non è nemmeno Java, sembra essere più rilevante per .NET
Photodeus,

3
-1. Downvoted b / c è tangenziale. E non necessariamente lo stile migliore per gli immutabili. Questo stile è più dettagliato delle semplici chiamate di metodo, in particolare per le operazioni concatenate. E rinuncia al polimorfismo di runtime perché chiama metodi statici, il che è significativo. YMMV.
Andrew Janke,

3

Entrambe le soluzioni funzionano per simulare printf, ma in modo diverso. Ad esempio, per convertire un valore in una stringa esadecimale, sono disponibili le 2 soluzioni seguenti:

  • con format(), il più vicino a 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;
    }
  • con replace(char oldchar , char newchar), un po 'più veloce ma piuttosto limitato:

        ...
        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)));
        ...
  • Esiste una terza soluzione che consiste nell'aggiungere il carattere retuno per uno (i caratteri sono numeri che si aggiungono l'un l'altro !) Come in:

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

... ma sarebbe davvero brutto.


Tutte grandi idee, ma trasforma il tuo codice in codice di sola scrittura impossibile da capire per il tuo collega.
Ben

0

È possibile eseguire una stampa su qualsiasi cosa sia OutputStream con PrintStream. In qualche modo, stampando in un flusso di stringhe:

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();

Il flusso di stringhe può essere creato in questo modo ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.