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
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:
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).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.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é,
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.
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);
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.
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.
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();
}
}