0

I'm reading csv file:

string line;

StreamReader sr = new StreamReader(file.ToString());

while ((line = sr.ReadLine()) != null)
{
    string col1 = line.Split(',')[10]; //old value
    col1 = "my value"; //new value
}

sr.Close();
sr.Dispose();

I want to replace old value by the new.

Then I need to save the file with the changes.

How can I do that?

3
  • Side note: using (StreamReader sr = new StreamReader(file.ToString())) {...} is far better (no resource leakage on exception, more readable and maintable) then explit Dispose call Commented Jun 28, 2019 at 11:12
  • Do you want to same modified values into the same file? Commented Jun 28, 2019 at 11:17
  • exactly: open file, take value and replace by new, save. Commented Jun 28, 2019 at 11:20

2 Answers 2

3

I suggest using File class instead of Streams and Readers. Linq is very convenient when querying data:

var modifiedData = File
  .ReadLines(file.ToString())
  .Select(line => line.Split(',')) 
  .Select(items => {
     //TODO: put relevant logic here: given items we should return csv line
     items[10] = "my value";

     return string.Join(",", items);
   })
  .ToList(); // <- we have to store modified data in memory

File.WriteAllLines(file.ToString(), modifiedData);

Another possibility (say, when initial file is too long to fit memory) is to save the modified data into a temporary file and then Move it:

 var modifiedData = File
  .ReadLines(file.ToString())
  .Select(line => line.Split(',')) 
  .Select(items => {
     //TODO: put relevant logic here: given items we should return csv line
     items[10] = "my value";

     return string.Join(",", items);
   });

 string tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.tmp");

 File.WriteAllLines(tempFile, modifiedData);

 File.Delete(file.ToString());
 File.Move(tempFile, file.ToString());
Sign up to request clarification or add additional context in comments.

4 Comments

thank you @Dmitry, I will try to use first mode. As I have some logic inside Stream I need to little modify code. Anyway thank you for example!
when write it to StreamWriter : sw.WriteLine(modifiedData); Im getting System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.String,System.String]....but how to get a Value?
modifiedData is IEnumerable<string>, not a single string. You can put a loop, e.g. foreach (var line in modifiedData) {sw.WriteLine(line);}
@4est: you can read the last line with a help of Last() or LastOrDefault(), e.g. string lastLine = modifiedData.Last();
0

Reading an entire file at once is memory-expensive. Not to mention creating its parallel copy. Using streams can fix it. Try this:

void Modify()
{
    using (var fs = new FileStream(file, FileMode.Open, FileAccess.ReadWrite))
    {
        string line;
        long position;

        while ((line = fs.ReadLine(out position)) != null)
        {
            var tmp = line.Split(',');
            tmp[1] = "00"; // new value
            var newLine = string.Join(",", tmp);
            fs.WriteLine(position, newLine);
        }
    }
}

with extensions:

static class FileStreamExtensions
{
    private static readonly char[] newLine = Environment.NewLine.ToCharArray();
    private static readonly int length = Environment.NewLine.Length;
    private static readonly char eof = '\uFFFF';

    public static string ReadLine(this FileStream fs, out long position)
    {
        position = fs.Position;
        var chars = new List<char>();
        char c;
        while ((c = (char)fs.ReadByte()) != eof && (chars.Count < length || !chars.Skip(chars.Count - 2).SequenceEqual(newLine)))
        {
            chars.Add(c);
        }
        fs.Position--;

        if (chars.Count == 0)
            return null;

        return new string(chars.ToArray());
    }

    public static void WriteLine(this FileStream fs, long position, string line)
    {
        var bytes = line.ToCharArray().Concat(newLine).Select(c => (byte)c).ToArray();
        fs.Position = position;
        fs.Write(bytes, 0, bytes.Length);
    }
}

The shortcoming is you must keep your values the same length. E.g. 999 and __9 are both of length 3. Fixing this makes things much more complicated, so I'd leave it this way.

Full working example

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.