重新启动计算机后,文件包含空值而不是JSON对象

时间:2019-01-07 16:53:15

标签: c# json file

我正在一个C#项目中,其中有几个配置文件。这些文件均包含一个JSON对象。在程序的整个生命周期中,可以在不同的时刻读取或写入这些文件。

此程序管理一台工业机器,由于各种原因,该机器可以随时关闭。关闭计算机电源会立即关闭正在运行我的程序的计算机。该计算机运行的Windows 10 Pro x64具有NTFS格式的SSD。

打开机器电源,因此我的程序重新启动时,在读取配置文件时告诉该文件不包含任何JSON对象,它将引发异常。当我用记事本打开文件时,该文件实际上是“空”的。 例如,代替使用JSON对象:

  

{       “核心价值   }

我具有以下内容:

  

NULNULNULNULNULNULNULNUL等

无论文件包含JSON对象还是“空”,文件的属性显示相同的文件大小,磁盘属性的大小也相同。

我还有其他可读写的配置文件,但都是纯文本格式,不受影响。

在每次关闭电源/打开电源时都不会出现此问题,并且不会影响每个配置文件。它通常与同一文件一起出现,但并不总是如此。

每当我读写配置文件时,我都会检查它们是否正确关闭:

读取文件:

  

JObject jsondata = JObject.Parse(File.ReadAllText(Path));

写文件:

  

File.WriteAllText(Path,jsondata.ToString());

两个方法(ReadAllText和WriteAllText)都指定它们打开,读取和关闭

这些方法被try catch子句包围着,我从来没有遇到过错误的JSON结构或NULL对象的问题。如果我是正确的话,即使是NULL JSON对象,也至少会将方括号{}写入文件中。

我尝试以编程方式将我的配置文件备份到另一个文件夹中。备份文件无需读取文件即可完成(使用File.Copy()方法):

  • 定期(每10分钟)用最新的配置文件更新备份文件。

  • 如果配置文件为“空”(通过检查文件中的所有字节是否等于0),则将其替换为相应的备份文件。

        // Check if any file has been modified since last check
        for (int file = 0; file < Directory.GetFiles(_FolderToBackup).Length; ++file)
        {
            // Get file to check
            string FilePath = Directory.GetFiles(_FolderToBackup)[file];
            string FileName = Path.GetFileName(FilePath);
    
            // Check if backup file with same name exists in Backup folder
            if (BackupFileExist(FileName))
            {
                // File path to backup file
                string BackupFilePath = _BackupFolder + "\\" + FileName;
    
                // If backup file is empty
                if (isFileEmpty(BackupFilePath))
                {
                    Log.Write("File " + FilePath + " is empty");
    
                    // Copy file to backupfolder, we don't have to check if file to backup is empty, because destination is already empty !
                    File.Copy(FilePath, BackupFilePath, true);
                }
    
                // If file to backup is empty
                if (isFileEmpty(FilePath))
                {
                    Log.Write("File " + FilePath + " is empty");
    
                    // Copy backup file back to folder to backup
                    File.Copy(BackupFilePath, FilePath, true);
                }
    
                // If no file is empty, update only files that have been modified since last check
                if(new FileInfo(FilePath).LastWriteTime > new FileInfo(BackupFilePath).LastWriteTime)
                {
                    File.Copy(FilePath, BackupFilePath, true);
                }
            }
    
            // If backup file does not exist
            else
            {
                string BackupFilePath = Path.Combine(_BackupFolder, FileName);
                File.Copy(FilePath, BackupFilePath);
            }
        }
    

当配置文件为“空”时,此周转效果很好。 但是,有时当我关闭/打开计算机时,配置文件及其备份文件都是空的。

即使在我的代码未运行时关闭电源的情况下,我也设法在机器重启时获得了一个空的配置文件。

在这一点上,我不知道我的问题是否与关机/开机或读取/写入文件的方式有关:

  • 为什么关闭/打开计算机时会发生这种情况?

  • 为什么只影响我的JSON配置文件?

  • 为什么要清空文件而不损坏文件?

  • 即使我的程序中文件未打开,为什么还会发生?

非常感谢您的宝贵时间。

2 个答案:

答案 0 :(得分:2)

File.WriteAllText()的{​​{3}}看,您的数据似乎是缓冲的受害者(似乎是1K的缓冲区大小)。如果要保证立即写入磁盘,则需要自己的方法:

    using (Stream stream = File.Create(yourPath, 64 * 1024, FileOptions.WriteThrough))
    using (TextWriter textWriter = new StreamWriter(stream))
    {
        textWriter.Write(jsonData);
    }

答案 1 :(得分:0)

非权威性的答案,但是在谷歌搜索“非原子写窗口”时,我偶然发现了一篇非常有趣的文章,该文章表明即使在NTFS上,您所遇到的情况也相当正常:https://blogs.msdn.microsoft.com/adioltean/2005/12/28/how-to-do-atomic-writes-in-a-file/

如果我正确理解,那么对于您的用例,建议您这样做:

  • 将您的写(您的JSON配置文件写)写到一个临时文件
    • (如果此处电源中断,则您刚刚丢失了这一轮更改,原始文件就可以了)
  • “清空写入内容”(不确定在您的环境中执行该操作的正确方法,但是此问题恰好探索了How to ensure all data has been physically written to disk?),或者使用FileOptions.WriteThrough进行写入,如@概述杰西·斯切尔
    • (如果此处电源中断,则您刚刚丢失了这一轮更改,原始文件就可以了)
  • 将原始文件重命名为“我知道我在做危险的事情”的命名格式,例如带有特定的后缀
    • (如果此处电源中断,则您没有主配置文件,则丢失了这一轮更改,但仍可以找到备份文件)
  • 将临时文件重命名为最终名称/原始名称
    • (如果此处断电,则您有一个主要的更新配置文件和一个过时的冗余“临时重命名”文件)
  • 删除临时重命名的文件

所有这些当然都假定您能够在开始重命名之前确保临时文件已完全写入。如果您已经解决了这个问题,那么启动时的过程将类似于:

  • 如果找到“临时重命名”文件,则将其删除(如果还有“主文件”),或将其重命名为主文件名
  • 加载主文件(永远不会损坏)