为什么我的文件有时会在读取或写入文件的过程中消失?

Why does my file sometimes disappear in the process of reading from it or writing to it?

我有一个应用程序可以读取文本文件以确定应生成哪些报告。它在大多数时间都可以正常工作,但偶尔,程序会删除它读取 from/writes 的其中一个文本文件。然后抛出异常(“找不到文件”)并且进度停止。

这是一些相关的代码。

首先,从文件中读取:

List<String> delPerfRecords = ReadFileContents(DelPerfFile);

. . .

private static List<String> ReadFileContents(string fileName)
{
    List<String> fileContents = new List<string>();
    try
    {
        fileContents = File.ReadAllLines(fileName).ToList();
    }
    catch (Exception ex)
    {
        RoboReporterConstsAndUtils.HandleException(ex);
    }
    return fileContents;
}

然后,写入文件 -- 它会将该文件中的 record/line 标记为已处理,以便下次检查该文件时不会重新生成相同的报告:

MarkAsProcessed(DelPerfFile, qrRecord);

. . .

private static void MarkAsProcessed(string fileToUpdate, string 
qrRecord)
{
    try
    {
        var fileContents = File.ReadAllLines(fileToUpdate).ToList();
        for (int i = 0; i < fileContents.Count; i++)
        {
            if (fileContents[i] == qrRecord)
            {
                fileContents[i] = string.Format("{0}{1} {2}"
qrRecord, RoboReporterConstsAndUtils.COMPLETED_FLAG, DateTime.Now);
            }
        }
        // Will this automatically overwrite the existing?
        File.Delete(fileToUpdate);
        File.WriteAllLines(fileToUpdate, fileContents);
    }
    catch (Exception ex)
    {
        RoboReporterConstsAndUtils.HandleException(ex);
    }
}

所以我确实删除了文件,但立即替换了它:

File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);

正在读取的文件内容如下:

Opas,20170110,20161127,20161231-COMPLETED 1/10/2017 12:33:27 AM
Opas,20170209,20170101,20170128-COMPLETED 2/9/2017 11:26:04 AM
Opas,20170309,20170129,20170225-COMPLETED
Opas,20170409,20170226,20170401

如果“-COMPLETED”出现在 record/row/line 的末尾,它会被忽略 - 不会被处理。

此外,如果第二个元素(在索引 1 处)是未来的日期,则不会处理(尚未)。

因此,对于上面显示的这些示例,前三个已经完成,随后将被忽略。第四个要到2017年4月9日或之后才会执行(届时会调取最后两个日期数据范围内的数据)。

为什么文件有时会被删除?我能做些什么来防止它发生?

如果有帮助,在更多上下文中,逻辑是这样的:

internal static string GenerateAndSaveDelPerfReports()
{
    string allUnitsProcessed = String.Empty;
    bool success = false;
    try
    {
        List<String> delPerfRecords = ReadFileContents(DelPerfFile);
        List<QueuedReports> qrList = new List<QueuedReports>();
        foreach (string qrRecord in delPerfRecords)
        {
            var qr = ConvertCRVRecordToQueuedReport(qrRecord);
            // Rows that have already been processed return null
            if (null == qr) continue;
            // If the report has not yet been run, and it is due, add i
to the list
            if (qr.DateToGenerate <= DateTime.Today)
            {
                var unit = qr.Unit;
                qrList.Add(qr);
                MarkAsProcessed(DelPerfFile, qrRecord);
                if (String.IsNullOrWhiteSpace(allUnitsProcessed))
                {
                    allUnitsProcessed = unit;
                }
                else if (!allUnitsProcessed.Contains(unit))
                {
                    allUnitsProcessed = allUnitsProcessed + " and "  
unit;
                }
            }
        }
        foreach (QueuedReports qrs in qrList)
        {
            GenerateAndSaveDelPerfReport(qrs);
            success = true;
        }
    }
    catch
    {
        success = false;
    }
    if (success)
    {
        return String.Format("Delivery Performance report[s] generate
for {0} by RoboReporter2017", allUnitsProcessed);
    }
    return String.Empty;
}

我如何固定此代码以防止文件被定期丢弃?

更新

我无法真正测试这个,因为这个问题很少发生,但我想知道在 File.Delete() 和 File.WriteAllLines() 之间添加一个“暂停”是否可以解决有问题吗?

更新 2

我不确定我的问题的答案是什么,所以我不会添加这个作为答案,但我的猜测是 File.Delete() 和 File.WriteAllLines( ) 之间的距离太近,因此删除有时会同时发生在文件的旧副本和新副本上。

如果是这样,两次调用之间的暂停可能已经解决了 99.42% 的问题,但根据我发现的 here,似乎 File.Delete() 是 redundant/superfluous 无论如何,所以我用注释掉的 File.Delete() 进行了测试,它工作正常;所以,我现在只是在没有那个偶尔有问题的电话。我希望这能解决问题。

// Will this automatically overwrite the existing?
File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);

我会简单地向 WriteAllLines() 添加一个额外的参数(默认为 false)来告诉函数以覆盖模式打开文件,而不是调用 File.Delete()就这样吧。

您当前是否检查打开文件的 return 值?


Update:好的,看起来 WriteAllLines() 是一个 .Net Framework 函数,因此无法更改,所以我删除了这个答案。但是现在这显示在评论中,作为另一个论坛上的建议解决方案:

"just use something like File.WriteAllText where if the file exists, the data is just overwritten, if the file does not exist it will be created."

这正是我的意思(虽然认为 WriteAllLines() 是用户定义的函数),因为我过去也遇到过类似的问题。

所以,这样的解决方案可以解决一些棘手的问题(而不是 deleting/fast 重新打开,只是覆盖文件)- OS 的工作也更少,可能 file/disk碎片化。