J'ai des fichiers texte sur lesquels j'ai besoin de pouvoir écrire à la volée, et assez souvent. Ces fichiers peuvent devenir assez volumineux, mais le texte à l'intérieur peut être complètement différent entre deux itérations.
Cela signifie que le texte peut être plus court qu'il ne l'était auparavant. Ce qui, si je n'effaçais pas d'abord les anciennes données, finirait par un mélange des anciennes données ajoutées à la fin de mes nouvelles données. Comme indiqué par la documentation MSDN.
Si vous écrasez une chaîne plus longue (telle que "Ceci est un test de la méthode OpenWrite") par une chaîne plus courte (telle que "Second run"), le fichier contiendra un mélange des chaînes ("Second runtest of la méthode OpenWrite ").
Cependant, la documentation ne spécifie pas de moyen de remédier ou même d'empêcher que cela se produise.
Actuellement, je fais ce qui suit:
File.WriteAllText(path, string.Empty);
using (Stream file = File.OpenWrite(path))
{
file.Write(dataToWrite, 0, dataToWrite.Length);
}
4,75589 ms en moyenne. WriteAllBytes a pris 4,28946 ms moyenne. file.Write et la troncature a pris 4,14433 ms en moyenne (et est la plus rapide / la plus cohérente avec cela). File.Delete l'ancien fichier et la création d'un nouveau fichier à l'aide de FileStream.Write prenait 5,31883 ms en moyenne. 812726 ms (Même si je dois admettre que cela pourrait très bien être dû à une mauvaise implémentation de ma part, je ne suis en aucun cas très expérimenté dans le multi-threading) notez que ces résultats s'appliquent à mon implémentation et le matériel. Les résultats peuvent varier selon le matériel.
3 Réponses :
Vous pouvez simplement écrire:
File.WriteAllBytes(path, dataToWrite);
Selon MSDN :
Crée un nouveau fichier, écrit le tableau d'octets spécifié dans le fichier et puis ferme le fichier. Si le fichier cible existe déjà, il est écrasé.
Vous pouvez utiliser la méthode WriteAllBytes . J'ai mis à jour la réponse.
Bien que je pense que cela a l'air bien plus élégant, c'est sûr. Cela semble en fait être plus lent. Après avoir exécuté un test de base 5 fois (copie d'environ 185 Mo de données 10 fois, dans un fichier de 192 Mo), il est environ 9% plus lent, bien que les temps soient plus cohérents. (test + résultats pastebin.com/p3DaqVBB )
@remy_rm Pas besoin de tests. Il est implémenté presque de la même manière que le vôtre.
Je recommanderais de tronquer le fichier:
using (FileStream file = File.OpenWrite(path))
{
file.Write(dataToWrite, 0, dataToWrite.Length);
file.SetLength(dataToWrite.Length);
}
Vous devriez tester si cela fonctionne mieux que d'écrire un nouveau fichier, de supprimer l'ancien fichier et de renommer le nouveau fichier avec l'ancien nom.
/ p>
Accepter cela comme la réponse car cela semble être la méthode la plus rapide (et la plus cohérente!) Pour écrire dans un fichier et effacer l'ancien contenu
Comme je l'ai mentionné, une approche multithread ressemblerait à ceci:
class FileWriter
{
private int index;
private string fileName = "file.txt";
private readonly object obj = new object();
private string FileName { get { lock (obj) { return fileName + index; } } }
public void Write(string content)
{
lock (obj)
{
int deleteIndex = index;
new Thread(() => DeleteOldFile(deleteIndex)).Start();
index++;
new Thread(() => File.WriteAllText(fileName + index, content)).Start();
}
}
public string GetFileContent()
{
lock (obj)
{
return File.ReadAllText(FileName);
}
}
private void DeleteOldFile(int fileNumber)
{
var fileToBeDeleted = fileName + fileNumber;
if (File.Exists(fileToBeDeleted))
File.Delete(fileToBeDeleted);
}
}
Remarque: je ne garantis pas le comportement correct de ce code, car je ne l'ai pas testé. p>
cette approche est en effet bonne lorsque j'ai besoin de travailler plusieurs fichiers à la fois! Cependant, il perd le gain qu'il obtient du multithreading lorsqu'un seul fichier est édité plusieurs fois car il ne peut pas écrire tant que la suppression n'est pas terminée.
@remy_rm le fait est que vous commencez à écrire en même temps que la suppression se produit donc cela peut être plus rapide qu'un simple écrasement du fichier.
Hmm, car cela génère une erreur Le processus ne peut pas accéder au fichier . Je suppose que je pourrais le modifier pour qu'il supprime l'ancien fichier, tout en créant le nouveau fichier avec un nom temporaire, puis le renommer en l'ancien nom de fichier. (Je vois maintenant que votre exemple a incrémenté l'index pour y parvenir, il me suffirait d'ajouter quelque chose qui restaure le nom de fichier d'origine). Bien que je pense que cela créerait une condition de concurrence si l'écriture / le changement de nom se produit avant la fin de la suppression? (assez nouveau pour le multi-threading, veuillez donc me corriger si je me trompe)
@remy_rm Je suppose que l'exception est levée à cause de l'index qui est le même lorsqu'il est envoyé au fil de suppression que dans le fil d'écriture (car il est capturé dans une fermeture), et il est incrémenté avant le début de la suppression. Je crois que le maintenir à une autre variable fonctionnerait. Je modifierai la réponse pour refléter ce changement.
Oui, ça marche maintenant! Je vais faire des tests sur un échantillon plus grand maintenant. Merci pour ton aide!
@remy_rm Je suis content d'entendre que ça marche! Faites-moi savoir comment vos tests se sont déroulés.
La réponse de Falcon semble être la plus rapide et la plus cohérente sur plusieurs tailles de fichiers. En raison du temps limité, je ne peux pas exécuter autant de tests / recueillir autant de données que je le souhaite. Mais j'espère être en mesure de former des tableaux de données pour le sauvegarder à un moment donné.
Avez-vous essayé de comparer les performances avec File.Delete et de recréer un nouveau fichier pour écrire tout le texte? C'est une question intéressante. J'aurais imaginé qu'il y aurait une option pour remplacer le fichier entier lors de l'écriture de tout le texte :)
File.WriteAllBytes doit faire ce que vous voulez en une seule ligne.
@Joe Merci! Je ne savais pas que
WriteAllBytesest aussi une chose. Cela semble beaucoup plus propre et c'est plus rapide. En exécutant un petit cas de test en téléchargeant un fichier texte de 6,5 Mo, il fonctionnait déjà 2 ms plus rapidement (105 ms contre 103 ms). (Je vois qu'Anderson vient de modifier sa réponse pour utiliser Bytes aswel)@remy_rm vous pouvez accepter la réponse d'Anderson Pimentel.
@Joe Bien que cela semble être plus rapide à petite échelle, il semble en fait moins performant lorsqu'il s'agit de fichiers plus volumineux. J'ai inclus un cas de test dans ma question initiale.
@remy_rm - en regardant l'implémentation avec ILSpy, il y a une différence mineure:
File.OpenWriteouvrira le fichier avecFileShare.None, tandis queFile.WriteAllBytes < / code> utiliseraFileShare.Read. Bizarre que le comportement par défaut soit différent, cela pourrait expliquer la petite différence que vous voyez.J'opterais pour une approche multi-threadée et multi-fichiers. Avoir un fil d'écriture dans un nouveau fichier puis un autre fil supprimant l'ancien et ainsi de suite. Cela vaut peut-être la peine d'être testé.