web-dev-qa-db-fra.com

Meilleure façon d'écrire une chaîne dans un fichier en utilisant Java nio

J'ai besoin d'écrire (ajouter) une énorme chaîne dans un fichier plat en utilisant Java nio. L'encodage est ISO-8859-1.

Actuellement, nous écrivons comme indiqué ci-dessous. Existe-t-il une meilleure manière de faire de même?

public void writeToFile(Long limit) throws IOException{
     String fileName = "/xyz/test.txt";
     File file = new File(fileName);        
     FileOutputStream fileOutputStream = new FileOutputStream(file, true);  
     FileChannel fileChannel = fileOutputStream.getChannel();
     ByteBuffer byteBuffer = null;
     String messageToWrite = null;
     for(int i=1; i<limit; i++){
         //messageToWrite = get String Data From database
         byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset.forName("ISO-8859-1")));
         fileChannel.write(byteBuffer);         
     }
     fileChannel.close();
}

EDIT: J'ai essayé les deux options. Voici les résultats.

@Test
public void testWritingStringToFile() {
    DiagnosticLogControlManagerImpl diagnosticLogControlManagerImpl = new DiagnosticLogControlManagerImpl();
    try {
        File file = diagnosticLogControlManagerImpl.createFile();
        long startTime = System.currentTimeMillis();
        writeToFileNIOWay(file);
        //writeToFileIOWay(file);
        long endTime = System.currentTimeMillis();
        System.out.println("Total Time is  " + (endTime - startTime));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

/**
 *
 * @param limit
 *            Long
 * @throws IOException
 *             IOException
 */
public void writeToFileNIOWay(File file) throws IOException {
    FileOutputStream fileOutputStream = new FileOutputStream(file, true);
    FileChannel fileChannel = fileOutputStream.getChannel();
    ByteBuffer byteBuffer = null;
    String messageToWrite = null;
    for (int i = 1; i < 1000000; i++) {
        messageToWrite = "This is a test üüüüüüööööö";
        byteBuffer = ByteBuffer.wrap(messageToWrite.getBytes(Charset
            .forName("ISO-8859-1")));
        fileChannel.write(byteBuffer);
    }
}

/**
 *
 * @param limit
 *            Long
 * @throws IOException
 *             IOException
 */
public void writeToFileIOWay(File file) throws IOException {
    FileOutputStream fileOutputStream = new FileOutputStream(file, true);
    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
        fileOutputStream, 128 * 100);
    String messageToWrite = null;
    for (int i = 1; i < 1000000; i++) {
        messageToWrite = "This is a test üüüüüüööööö";
        bufferedOutputStream.write(messageToWrite.getBytes(Charset
            .forName("ISO-8859-1")));
    }
    bufferedOutputStream.flush();
    fileOutputStream.close();
}

private File createFile() throws IOException {
    File file = new File(FILE_PATH + "test_sixth_one.txt");
    file.createNewFile();
    return file;
}

Utilisation de ByteBuffer et Channel: a pris 4402 ms

Utilisation de Writer tamponné: 563 ms

37
nobody

Je ne pense pas que vous pourrez obtenir une réponse stricte sans comparer votre logiciel. NIO peut accélérer considérablement l'application dans les bonnes conditions, mais cela peut également ralentir les choses. Voici quelques points:

  • Avez-vous vraiment besoin de cordes? Si vous stockez et recevez des octets de votre base de données, vous pouvez éviter tous les coûts d'allocation de chaîne et d'encodage.
  • Avez-vous vraiment besoin de rewind et flip? On dirait que vous créez un nouveau tampon pour chaque chaîne et que vous l'écrivez simplement sur le canal. (Si vous optez pour la voie NIO, comparez les stratégies qui réutilisent les tampons au lieu d'envelopper/jeter, je pense qu'ils feront mieux).
  • Gardez à l'esprit que wrap et allocateDirect peuvent produire des tampons assez différents. Benchmark à la fois pour saisir les compromis. Avec l'allocation directe, assurez-vous de réutiliser le même tampon afin d'obtenir les meilleures performances.
  • Et la chose la plus importante est: assurez-vous de comparer NIO avec BufferedOutputStream et/ou BufferedWritter approches (utilisez un intermédiaire byte[] Ou char[] tampon avec une taille raisonnable également). J'ai vu beaucoup , beaucoup , beaucoup des gens découvrant que NIO n'est pas une solution miracle.

Si vous avez envie d'un bord qui saigne ... Retour à IO Trails pour certains NIO2: D.

Et voici un référence intéressante sur la copie de fichiers en utilisant différentes stratégies . Je sais que c'est un problème différent, mais je pense que la plupart des faits et des conclusions des auteurs s'appliquent également à votre problème.

À votre santé,

MISE À JOUR 1:

Puisque @EJP m'a informé que les tampons directs ne seraient pas efficaces pour ce problème, je l'ai moi-même évalué et je me suis retrouvé avec une solution Nice NIO utilisant des fichiers mappés avec Nemory. Dans mon Macbook exécutant OS X Lion, cela bat BufferedOutputStream avec une solide marge. mais gardez à l'esprit que cela peut être OS/Matériel/VM spécifique:

public void writeToFileNIOWay2(File file) throws IOException {
    final int numberOfIterations = 1000000;
    final String messageToWrite = "This is a test üüüüüüööööö";
    final byte[] messageBytes = messageToWrite.
            getBytes(Charset.forName("ISO-8859-1"));
    final long appendSize = numberOfIterations * messageBytes.length;
    final RandomAccessFile raf = new RandomAccessFile(file, "rw");
    raf.seek(raf.length());
    final FileChannel fc = raf.getChannel();
    final MappedByteBuffer mbf = fc.map(FileChannel.MapMode.READ_WRITE, fc.
            position(), appendSize);
    fc.close();
    for (int i = 1; i < numberOfIterations; i++) {
        mbf.put(messageBytes);
    }
} 

J'avoue que j'ai un peu triché en calculant au préalable la taille totale à ajouter (environ 26 Mo). Cela peut ne pas être possible pour plusieurs scénarios du monde réel. Néanmoins, vous pouvez toujours utiliser une taille d'ajout "suffisamment grande pour les opérations et tronquer ultérieurement le fichier.

MISE À JOUR 2 (2019):

Pour tous ceux qui recherchent une solution moderne (comme dans, Java 11+) au problème, je suivrais @ les conseils de DodgyCodeException et utiliser Java.nio.file.Files.writeString :

String fileName = "/xyz/test.txt";
String messageToWrite = "My long string";
Files.writeString(Paths.get(fileName), messageToWrite, StandardCharsets.ISO_8859_1);
15
Anthony Accioly

Il existe une solution ne ligne, utilisant Java nio:

Java.nio.file.Files.write(Paths.get(file.toURI()), 
                          "My string to save".getBytes("utf-8"),
                          StandardOpenOption.CREATE,
                          StandardOpenOption.TRUNCATE_EXISTING);

Je n'ai pas testé cette solution avec les autres, mais l'utilisation de l'implémentation intégrée pour le fichier ouvert-écrit-fermé devrait être rapide et le code est assez petit.

54
Roberto

Un BufferedWriter autour d'un FileWriter sera presque certainement plus rapide que n'importe quel schéma NIO que vous pouvez trouver. Votre code n'est certainement pas optimal, avec un nouveau ByteBuffer par écriture, puis en effectuant des opérations inutiles quand il est sur le point de sortir du cadre, mais en tout cas votre question est fondée sur une idée fausse. NIO ne décharge pas du tout l'empreinte mémoire sur le système d'exploitation, sauf si vous utilisez FileChannel.transferTo/From (), ce que vous ne pouvez pas dans ce cas.

NB n'utilisez pas un PrintWriter comme suggéré dans les commentaires, car cela avale les exceptions. PW est vraiment uniquement pour les consoles et les fichiers journaux où vous ne vous souciez pas.

8
user207421

Voici un moyen court et simple. Il crée un fichier et écrit les données relatives à votre projet de code:

private void writeToFile(String filename, String data) {
    Path p = Paths.get(".", filename);
    try (OutputStream os = new BufferedOutputStream(
        Files.newOutputStream(p, StandardOpenOption.CREATE, StandardOpenOption.APPEND))) {
        os.write(data.getBytes(), 0, data.length());
    } catch (IOException e) {
        e.printStackTrace();
    }
}
0
darkhorse21