web-dev-qa-db-fra.com

Modifier une ligne spécifique d'un fichier texte en C #

J'ai deux fichiers texte, Source.txt et Target.txt. La source ne sera jamais modifiée et contiendra N lignes de texte. Donc, je veux supprimer une ligne de texte spécifique dans Target.txt et la remplacer par une ligne de texte spécifique de Source.txt, je sais de quel nombre de ligne j'ai besoin, en fait la ligne numéro 2, les deux fichiers.

J'ai quelque chose comme ça:

string line = string.Empty;
int line_number = 1;
int line_to_edit = 2;

using (StreamReader reader = new StreamReader(@"C:\source.xml"))
{
    using (StreamWriter writer = new StreamWriter(@"C:\target.xml"))
    {
        while ((line = reader.ReadLine()) != null)
        {
            if (line_number == line_to_edit)
            {
                writer.WriteLine(line);
            } 

            line_number++;
        }
    }
}

Mais lorsque j'ouvre Writer, le fichier cible est effacé, il écrit les lignes, mais, lorsqu'il est ouvert, le fichier cible ne contient que les lignes copiées, le reste est perdu.

Que puis-je faire?

27
Luis

Vous ne pouvez pas réécrire une ligne sans réécrire tout le fichier (à moins que les lignes ne soient de la même longueur). Si vos fichiers sont petits, la lecture de l'intégralité du fichier cible dans la mémoire, puis sa réécriture peuvent être utiles. Vous pouvez faire ça comme ceci:

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        int line_to_edit = 2; // Warning: 1-based indexing!
        string sourceFile = "source.txt";
        string destinationFile = "target.txt";

        // Read the appropriate line from the file.
        string lineToWrite = null;
        using (StreamReader reader = new StreamReader(sourceFile))
        {
            for (int i = 1; i <= line_to_edit; ++i)
                lineToWrite = reader.ReadLine();
        }

        if (lineToWrite == null)
            throw new InvalidDataException("Line does not exist in " + sourceFile);

        // Read the old file.
        string[] lines = File.ReadAllLines(destinationFile);

        // Write the new file over the old file.
        using (StreamWriter writer = new StreamWriter(destinationFile))
        {
            for (int currentLine = 1; currentLine <= lines.Length; ++currentLine)
            {
                if (currentLine == line_to_edit)
                {
                    writer.WriteLine(lineToWrite);
                }
                else
                {
                    writer.WriteLine(lines[currentLine - 1]);
                }
            }
        }
    }
}

Si vos fichiers sont volumineux, il serait préférable de créer un nouveau fichier afin de pouvoir lire le streaming d'un fichier pendant que vous écrivez dans l'autre. Cela signifie que vous n'avez pas besoin d'avoir le fichier entier en mémoire à la fois. Vous pouvez faire ça comme ceci:

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        int line_to_edit = 2;
        string sourceFile = "source.txt";
        string destinationFile = "target.txt";
        string tempFile = "target2.txt";

        // Read the appropriate line from the file.
        string lineToWrite = null;
        using (StreamReader reader = new StreamReader(sourceFile))
        {
            for (int i = 1; i <= line_to_edit; ++i)
                lineToWrite = reader.ReadLine();
        }

        if (lineToWrite == null)
            throw new InvalidDataException("Line does not exist in " + sourceFile);

        // Read from the target file and write to a new file.
        int line_number = 1;
        string line = null;
        using (StreamReader reader = new StreamReader(destinationFile))
        using (StreamWriter writer = new StreamWriter(tempFile))
        {
            while ((line = reader.ReadLine()) != null)
            {
                if (line_number == line_to_edit)
                {
                    writer.WriteLine(lineToWrite);
                }
                else
                {
                    writer.WriteLine(line);
                }
                line_number++;
            }
        }

        // TODO: Delete the old file and replace it with the new file here.
    }
}

Vous pouvez ensuite déplacer le fichier une fois que vous êtes sûr que l'opération d'écriture a réussi (aucune excecption n'a été levée et le rédacteur est fermé).

Notez que dans les deux cas, il est un peu déroutant que vous utilisiez l'indexation basée sur 1 pour vos numéros de ligne. Il peut être plus judicieux dans votre code d'utiliser l'indexation basée sur 0. Vous pouvez avoir un index basé sur 1 dans votre interface utilisateur vers votre programme si vous le souhaitez, mais le convertir en un index indexé 0 avant de l'envoyer plus loin.

De plus, l'inconvénient de remplacer directement l'ancien fichier par le nouveau fichier est que s'il échoue à mi-chemin, vous risquez de perdre définitivement toutes les données qui n'ont pas été écrites. En écrivant d'abord dans un troisième fichier, vous ne supprimez les données d'origine qu'après avoir vérifié que vous en avez une autre copie (corrigée), afin de pouvoir récupérer les données si l'ordinateur tombe en panne à mi-chemin.

Une dernière remarque: j'ai remarqué que vos fichiers avaient une extension xml. Vous voudrez peut-être déterminer s'il est plus logique d'utiliser un analyseur XML pour modifier le contenu des fichiers au lieu de remplacer des lignes spécifiques.

43
Mark Byers

la manière la plus simple est:

static void lineChanger(string newText, string fileName, int line_to_edit)
{
     string[] arrLine = File.ReadAllLines(fileName);
     arrLine[line_to_edit - 1] = newText;
     File.WriteAllLines(fileName, arrLine);
}

utilisation:

lineChanger("new content for this line" , "sample.text" , 34);
38
Bruce Afruz

Lorsque vous créez un StreamWriter, il crée toujours un fichier à partir de zéro, vous devrez créer un troisième fichier et copier à partir de la cible et remplacer ce dont vous avez besoin, puis remplacer l'ancien. Mais comme je peux voir ce dont vous avez besoin, c'est de la manipulation XML, vous voudrez peut-être utiliser XmlDocument et modifier votre fichier à l'aide de Xpath.

3
Pablo Retyk

Je suppose que ce qui suit devrait fonctionner (au lieu de la partie écrivain de votre exemple). Je suis malheureusement sans environnement de construction donc c'est de mémoire mais j'espère que ça aide

using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite)))
        {
            var destinationReader = StreamReader(fs);
            var writer = StreamWriter(fs);
            while ((line = reader.ReadLine()) != null)
            {
              if (line_number == line_to_edit)
                {
                    writer.WriteLine(lineToWrite);
                }
                else
                {
                    destinationReader .ReadLine();
                }
                line_number++;
            }
        }
2
Rune FS

Vous devez ouvrir le fichier de sortie pour l'accès en écriture plutôt que d'utiliser un nouveau StreamReader, qui écrase toujours le fichier de sortie.

StreamWriter stm = null;
fi = new FileInfo(@"C:\target.xml");
if (fi.Exists)
   stm = fi.OpenWrite();

Bien sûr, vous devrez toujours rechercher la bonne ligne dans le fichier de sortie, ce qui sera difficile car vous ne pouvez pas lire à partir de celui-ci, donc à moins que vous ne connaissiez déjà le décalage d'octet à rechercher, vous souhaitiez probablement vraiment lire/écrire accès.

FileStream stm = fi.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);

avec ce flux, vous pouvez lire jusqu'à ce que vous arriviez au point où vous souhaitez apporter des modifications, puis écrire. Gardez à l'esprit que vous écrivez des octets, pas des lignes, donc pour écraser une ligne, vous devrez écrire le même nombre de caractères que la ligne que vous souhaitez modifier.

2
John Knoeller