1

I have a text file containing some information that I want to edit. The file looks something like this:

id: 31
name: Anna
profession: Doctor

I can read that entry with a StreamReader, and present it in my application. I then want the user to be able to change the name and profession of an entry, so I'd like to edit these specific rows to the new values, while keeping id intact (in my real code, there's not just a few rows, but a lot of rows where only some should be changed). So, for example, I want the file, at the end of my operation, to look like this.

id: 31
name: Emma
profession: Programmer

However, I also have to take into account that sometimes the rows don't exist beforehand. For example, before editing Anna to Emma, it's not certain that she had a profession, the file could have looked like this:

id: 31
name: Anna

And in that case, I want to add the line profession: Programmerto the end there.

I tried using a FileStreamwith ReadWrite access, that I give to a StreamReader and a StreamWriter, but then I found no way of changing or replacing a line of text, only reading it and writing a new identical line while keeping the old.

using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite))
using (StreamReader reader = new StreamReader(fileStream))
using (StreamWriter writer = new StreamWriter(fileStream))
{
    bool idExists = false;
    bool nameExists = false;
    bool tagsExist = false;

    string line;
    while((line = reader.ReadLine()) != null)
    {
        if (line.StartsWith("id:"))
            idExists = true;
        else if (line.StartsWith("name:"))
        {
            nameExists = true;
            line = $"name: {entryToSave.Name}";
            writer.WriteLine(line); // Will write an additional line and not replace
        }
        else if (line.StartsWith("profession:"))
        {
            professionExists = true;
            line = $"profession: {entryToSave.Profession}";
            writer.WriteLine(line); // Will write an additional line and not replace
        }
    }

    if (!idExists)
        writer.WriteLine($"id: {generatedId}");
    if (!nameExists)
        writer.WriteLine($"name: {entryToSave.Name}");
    if (!professionExists)
        writer.WriteLine($"profession: {entryToSave.Profession}");
}

I also tried using File.ReadAllLines, loop through the lines, and then write back all the lines to the file, only modifying the lines that were to be modified. However, I don't have access to the file through File.WriteAllLines for some reason that I don't understand, as a StreamWriter has access. Code:

var previousData = File.ReadAllLines(filePath);
var newData = new List<string>();
bool idExists = false;
bool nameExists = false;
bool professionExists = false;

for (int i = 0; i < previousData.Length; i++)
{
    var line = previousData[i];

    if (line.StartsWith("id:")
        idExists = true;
    else if (line.StartsWith("name:")
    {
        nameExists = true;
        line = $"name: {entryToSave.Name}";
    }
    else if (line.StartsWith("profession:"))
    {
        professionExists = true;
        line = $"profession: {entryToSave.Profession}";
    }

    newData.Add(line);
}

if (!idExists)
    newData.Add($"id: {generatedId}");
if (!nameExists)
    newData.Add($"name: {entryToSave.Name}");
if (!professionExists)
    newData.Add($"profession: {entryToSave.Profession}");

File.WriteAllLines(filePath, newData.ToArray()); // Access denied

How is this most easily achieved, without file streams interfering with each other?

Helena
  • 1,003
  • 2
  • 8
  • 21
  • I just found out myself now that the reason why I can't do File.WriteAllLines is not that the file is busy, access is denied anyway for some reason, that I don't know how to solve. I'll edit my question. – Helena Apr 02 '17 at 07:20
  • If you found my solution helpful I'd be very appreciative if you'd mark it as an answer :) – CitiZen Apr 03 '17 at 17:16
  • Oh, right, sorry, I forgot! Will do. – Helena Apr 04 '17 at 16:13

1 Answers1

0

If you've already presented the data to the user in entries, enabling the user to edit name and profession, you could just read the file, get the ID and fill the remainder of the file with the value of the entries. The following is an example console application.

static void Main(string[] args)
{
    var filePath = "test.txt";

    // Simulated input from user 
    // these should come from entries in the application?
    var name = "Foo"; 
    var profession = "Bar";

    var personData = new PersonData(); // class declared below

    using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite))
    using (StreamReader reader = new StreamReader(fileStream))
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            if (line.StartsWith("id:"))
                personData.ID = line;
        }
    } // Now reader and filestream is closed, file is available again.


    // You don't specify what you would like to happen if personData.ID is null, 
    // so I make an assumption the generatedId is what you'd like to use.
    if (string.IsNullOrWhiteSpace(personData.ID)
        personData.ID = $"id: {generatedId}"; 

    // Add the data from the entries
    personData.Name = $"name: {name}";
    personData.Profession = $"profession: {profession}";

    File.Delete(filePath); // remove the file

    using (FileStream fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
    using (StreamWriter writer = new StreamWriter(fileStream))
    {
        writer.WriteLine(personData.ID);
        writer.WriteLine(personData.Name);
        writer.WriteLine(personData.Profession);
    }
}
private class PersonData
{
    public string ID { get; set; }
    public string Name { get; set; }
    public string Profession { get; set; }
}

Now you just have to find out how to get access to the file if you're having permission problems.

CitiZen
  • 71
  • 7
  • 1
    Reading from one file, deleting it and writing to a new one indeed was the simplest way to do it! – Helena Apr 04 '17 at 16:14