代码之家  ›  专栏  ›  技术社区  ›  Nicola

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

  •  1
  • Nicola  · 技术社区  · 6 年前

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

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

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

    { “关键”:价值 }

    我有以下内容:

    努努努努努努努努尔等

    文件的属性显示相同的文件大小,不管它是否包含JSON对象或是“空的”,磁盘上的大小属性也是如此。

    我有其他读写的配置文件,但它们是纯文本,不受影响。

    此问题不会在每次关机/开机时出现,也不会影响每个配置文件。它通常显示在同一个文件中,但并不总是如此。

    我检查了配置文件在读写时是否正确关闭:

    读取文件:

    jobject jsondata=jobject.parse(file.readalltext(path));

    写入文件:

    file.writeAllText(path,jsondata.toString());

    这两种方法(readAllText和writeAllText)都指定它们打开、读取和 关闭 文件。

    这些方法被try-catch子句包围,我从未遇到过错误的JSON结构或空对象的问题。如果我是正确的,即使是一个空的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 回复  |  直到 6 年前
        1
  •  2
  •   Jesse C. Slicer    6 年前

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

        using (Stream stream = File.Create(yourPath, 64 * 1024, FileOptions.WriteThrough))
        using (TextWriter textWriter = new StreamWriter(stream))
        {
            textWriter.Write(jsonData);
        }
    
        2
  •  0
  •   Tao    6 年前

    我无意中发现了一篇非常有趣的文章,这篇文章指出,即使在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进行写入,如@jessec.slicler所述。
      • (如果这里的电源出现故障,您刚刚丢失了这一轮更改,原始文件就可以了)
    • 将原始文件重命名为“我知道我在做危险的事情”命名格式,例如使用特定的后缀
      • (如果这里的电源出现故障,您没有主配置文件,您已经丢失了这一轮更改,但仍然可以找到备份)
    • 将临时文件重命名为最终/原始名称
      • (如果这里的电源故障,您有一个更新的主配置文件和一个冗余的过时的“临时重命名”文件)
    • 删除临时重命名的文件

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

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