web-dev-qa-db-fra.com

Transfert d'un StringBuilder Java vers un fichier

Quel est le moyen le plus efficace/élégant de transférer un StringBuilder dans un fichier texte?

Tu peux faire:

outputStream.write(stringBuilder.toString().getBytes());

Mais est-ce efficace pour un très long fichier?

Y a-t-il un meilleur moyen?

34
Patrick

Comme d'autres l'ont fait remarquer, utilisez un Writer et un BufferedWriter, mais n'appelez pas alors writer.write(stringBuilder.toString()); mais plutôt writer.append(stringBuilder);.

EDIT: Mais, je vois que vous avez accepté une réponse différente parce que c'était un one-liner. Mais cette solution a deux problèmes:

  1. il n'accepte pas un Java.nio.Charset. MAL. Vous devez toujours spécifier explicitement un jeu de caractères.

  2. cela vous fait toujours souffrir d'une stringBuilder.toString(). Si vous recherchez vraiment la simplicité, essayez ce qui suit dans le projet Guava :

Files.write (stringBuilder, fichier, Charsets.UTF_8)

39

Vous devez utiliser un BufferedWriter pour optimiser les écritures (écrivez toujours des données de caractères en utilisant un Writer au lieu d'un OutputStream). Si vous n'étiez pas en train d'écrire des données de caractères, vous utiliseriez un BufferedOutputStream.

File file = new File("path/to/file.txt");
BufferedWriter writer = null;
try {
    writer = new BufferedWriter(new FileWriter(file));
    writer.write(stringBuilder.toString());
} finally {
    if (writer != null) writer.close();
}

ou, en utilisant try-with-resources (Java 7 et plus)

File file = new File("path/to/file.txt");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
    writer.write(stringBuilder.toString());
}

Étant donné que vous écrivez dans un fichier, une meilleure approche consisterait à écrire plus souvent dans BufferedWriter au lieu de créer un énorme StringBuilder en mémoire et de tout écrire à la fin (en fonction de votre cas d'utilisation, vous pourriez même pour éliminer complètement le StringBuilder). L'écriture incrémentielle pendant le traitement économisera de la mémoire et optimisera l'utilisation de votre bande passante d'E/S limitée, à moins qu'un autre thread ne tente de lire beaucoup de données du disque en même temps que vous écrivez.

23
rob

Eh bien, si la chaîne est énorme, toString().getBytes() créera des octets en double (2 ou 3 fois). La taille de la chaîne.

Pour éviter cela, vous pouvez extraire une partie de la chaîne et l'écrire en plusieurs parties.

Voici à quoi cela pourrait ressembler:

final StringBuilder aSB = ...;
final int    aLength = aSB.length();
final int    aChunk  = 1024;
final char[] aChars  = new char[aChunk];

for(int aPosStart = 0; aPosStart < aLength; aPosStart += aChunk) {
    final int aPosEnd = Math.min(aPosStart + aChunk, aLength);
    aSB.getChars(aPosStart, aPosEnd, aChars, 0);                 // Create no new buffer
    final CharArrayReader aCARead = new CharArrayReader(aChars); // Create no new buffer// This may be slow but it will not create any more buffer (for bytes)
    int aByte;
    while((aByte = aCARead.read()) != -1)
        outputStream.write(aByte);
}

J'espère que cela t'aides.

15
NawaMan

Vous pouvez utiliser la Apache Commons IO bibliothèque, qui vous donne FileUtils :

FileUtils.writeStringToFile(file, stringBuilder.toString(), Charset.forName("UTF-8"))
14
Mike Sickler

Pour mieux utiliser les données de caractères, utilisez Reader/Writer. Dans votre cas, utilisez une BufferedWriter. Si possible, utilisez BufferedWriter depuis le début au lieu de StringBuilder pour économiser de la mémoire.

Notez que votre façon d’appeler la méthode non-arg getBytes() utiliserait le codage de caractères par défaut de la plateforme pour décoder les caractères. Cela peut échouer si le codage par défaut de la plate-forme est par exemple ISO-8859-1 alors que vos données de chaîne contiennent des caractères en dehors du caractère ISO-8859-1. Il vaut mieux utiliser getBytes(charset) où vous pouvez spécifier vous-même le jeu de caractères, tel que UTF-8.

3
BalusC

Si la chaîne elle-même est longue, vous devez absolument éviter toString (), qui en fait une autre copie. Le moyen le plus efficace d’écrire en streaming devrait ressembler à ceci:

OutputStreamWriter writer = new OutputStreamWriter(
        new BufferedOutputStream(outputStream), "utf-8");

for (int i = 0; i < sb.length(); i++) {
    writer.write(sb.charAt(i));
}
1
ZZ Coder

Basé sur https://stackoverflow.com/a/1677317/980442

Je crée cette fonction qui utilise OutputStreamWriter et la write(), ceci est également optimisé pour la mémoire, mieux que d'utiliser simplement StringBuilder.toString().

public static void stringBuilderToOutputStream(
        StringBuilder sb, OutputStream out, String charsetName, int buffer)
        throws IOException {
    char[] chars = new char[buffer];
    try (OutputStreamWriter writer = new OutputStreamWriter(out, charsetName)) {
        for (int aPosStart = 0; aPosStart < sb.length(); aPosStart += buffer) {
            buffer = Math.min(buffer, sb.length() - aPosStart);
            sb.getChars(aPosStart, aPosStart + buffer, chars, 0);
            writer.write(chars, 0, buffer);
        }
    }
}
0
Daniel De León

Points de repère pour la plupart des réponses ici + implémentation améliorée: https://www.genuitec.com/dump-a-stringbuilder-to-file/

La mise en œuvre finale est dans les lignes de

try {
    BufferedWriter bw = new BufferedWriter(
            new OutputStreamWriter(
                    new FileOutputStream(file, append), charset), BUFFER_SIZE);
    try {
        final int length = sb.length();
        final char[] chars = new char[BUFFER_SIZE];
        int idxEnd;
        for ( int idxStart=0; idxStart<length; idxStart=idxEnd ) {
            idxEnd = Math.min(idxStart + BUFFER_SIZE, length);
            sb.getChars(idxStart, idxEnd, chars, 0);
            bw.write(chars, 0, idxEnd - idxStart);
        }
        bw.flush();
    } finally {
        bw.close();
    }
} catch ( IOException ex ) {
    ex.printStackTrace();
}
0
dotwin