【问题标题】:Replace value & save file during reading CSV file (C#)在读取 CSV 文件时替换值并保存文件(C#)
【发布时间】:2019-11-10 08:23:42
【问题描述】:

我正在读取 csv 文件:

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

我想用新值替换旧值。

然后我需要保存更改后的文件。

我该怎么做?

【问题讨论】:

  • 旁注:using (StreamReader sr = new StreamReader(file.ToString())) {...} 要好得多(异常时没有资源泄漏,更具可读性和可维护性)然后 explit Dispose 调用
  • 是否要将相同的修改值放入 same 文件中?
  • 正确:打开文件,取值并替换为新文件,保存。

标签: c# replace stream streamreader streamwriter


【解决方案1】:

我建议使用File 类而不是Streams 和Readers。 Linq 查询数据时非常方便:

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

另一种可能性(例如,当初始文件太长而无法容纳内存时)是将修改后的数据保存到临时文件,然后Move它:

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

【讨论】:

  • 谢谢@Dmitry,我会尝试使用第一种模式。由于我在 Stream 中有一些逻辑,因此我几乎不需要修改代码。无论如何,谢谢你!
  • 写入StreamWriter时:sw.WriteLine(modifiedData);我得到 System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.String,System.String]....但是如何获得一个值?
  • modifiedDataIEnumerable&lt;string&gt;,而不是单个 string。你可以放一个loop,例如foreach (var line in modifiedData) {sw.WriteLine(line);}
  • @4est:您可以在Last()LastOrDefault() 的帮助下阅读最后一行,例如string lastLine = modifiedData.Last();
【解决方案2】:

一次读取整个文件会占用大量内存。更不用说创建它的并行副本了。使用流可以修复它。试试这个:

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

带有扩展名:

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

缺点是你必须保持你的价值观相同的长度。例如。 999__9 的长度都是 3。解决这个问题会使事情变得更加复杂,所以我就这样吧。

Full working example

【讨论】: