web-dev-qa-db-ja.com

C#でテキストファイルの特定の行を編集する

Source.txtとTarget.txtの2つのテキストファイルがあります。ソースは変更されず、N行のテキストが含まれます。だから、Target.txtの特定のテキスト行を削除し、Source.txtの特定のテキスト行で置き換えたいと思います。必要な行数はわかっています。実際は、両方のファイルの行番号2です。

私はこのようなものを持っています:

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++;
        }
    }
}

しかし、Writerを開くと、ターゲットファイルは消去されて行が書き込まれますが、開いたとき、ターゲットファイルにはコピーされた行のみが含まれ、残りは失われます。

私に何ができる?

27
Luis

ファイル全体を書き換えずに行を書き換えることはできません(行が同じ長さでない限り)。ファイルが小さい場合は、ターゲットファイル全体をメモリに読み込んでから、再度書き出すことが理にかなっています。次のようにできます:

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]);
                }
            }
        }
    }
}

ファイルが大きい場合、新しいファイルを作成して、一方のファイルからストリーミングを読み取りながら他方のファイルに書き込むことができます。これは、ファイル全体を一度にメモリに入れる必要がないことを意味します。次のようにできます:

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.
    }
}

書き込み操作が成功したことを確認したら、後でファイルを移動できます(例外はスローされず、ライターは閉じられます)。

どちらの場合でも、行番号に1から始まるインデックスを使用しているのは少しわかりにくいことに注意してください。コードで0ベースのインデックスを使用する方が理にかなっている場合があります。必要に応じて、プログラムのユーザーインターフェイスに1から始まるインデックスを付けることができますが、さらに送信する前に0インデックスに変換します。

また、古いファイルを新しいファイルで直接上書きすることの欠点は、途中で失敗すると、書き込まれなかったデータが永久に失われる可能性があることです。最初に3番目のファイルに書き込むことで、元のデータを削除するのは、そのデータの別の(修正された)コピーがあることを確認してからです。

最後のコメント:ファイルの拡張子がxmlであることに気付きました。特定の行を置き換えるのではなく、XMLパーサーを使用してファイルの内容を変更する方が意味があるかどうかを検討することをお勧めします。

43
Mark Byers

最も簡単な方法は次のとおりです。

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);
}

使用法 :

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

StreamWriterを作成するとき、常に最初からファイルを作成します。3番目のファイルを作成し、ターゲットからコピーして必要なものを置き換えてから、古いファイルを置き換える必要があります。ただし、必要なのはXML操作であることがわかるので、XmlDocumentを使用し、Xpathを使用してファイルを変更することができます。

3
Pablo Retyk

(あなたの例の作家の部分の代わりに)以下が動作するはずです。私は残念ながらビルド環境がないので、メモリからのものですが、それが役立つことを願っています

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

常に出力ファイルを上書きする新しいStreamReaderを使用するのではなく、書き込みアクセスのために出力ファイルを開く必要があります。

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

もちろん、出力ファイル内の正しい行をシークする必要がありますが、読み取ることはできないため、難しいので、シークするバイトオフセットをすでに知っていない限り、おそらく読み取り/書き込みが必要です。アクセス。

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

このストリームを使用すると、変更したいポイントに到達するまで読み取り、書き込みができます。行ではなくバイトを書き込むので、行を上書きするには、変更する行と同じ数の文字を書き込む必要があることに注意してください。

2
John Knoeller