在 Nim 中是否有一个文件对象来获取文件的路径或名称?

Is there a file object to get path or name of a file in Nim?

比方说,我想用一个对象来表示一个文件,我想获取它的文件名(或路径),以便我可以使用该名称删除文件或用于其他标准图书馆手续。我想要一个单一的抽象,它可以与所有可用的文件相关的标准库过程一起使用。

我找到了 FileInfo,但在我的研究中我没有找到获取文件名的过程。 FileFileHandle 从软件工程的角度来看非常无用,因为它们没有提供方便的抽象并且没有成员。

Nim 中是否有一种文件抽象(对象),它提供对 FileInfo 以及文件名的快速访问,以便一个文件不需要多个过程参数?

在 Nim 或任何其他语言中没有这样的抽象,只是因为您要求对大多数文件系统做不可能的事情。考虑 FileInfo 结构及其 linkCount 字段,它告诉您文件对象具有的硬链接数。但是没有办法从一个或所有这些链接中获取文件名,除非自己构建和更新整个文件系统的数据库。

虽然大多数文件系统允许通过路径访问文件,但很少有文件系统提供文件的路径,因为它们实际上不需要路径!一个例子是 Unix 文件系统,其中一个进程通过路径打开文件,然后在不关闭文件的情况下删除路径。虽然保持文件打开的进程处于活动状态,但该文件实际上不会消失,因此您会遇到文件没有路径的情况。

处理路径的问题,尤其是考虑到跨平台应用程序,涉及其自身的问题:如果将路径存储为字符串,路径分隔符是什么以及如何 转义 它?您的文件系统是否支持需要特殊情况处理的卷?路径使用什么字符串编码来满足所有用户?仅仅是编码问题就需要大量的表格和转换,这会使其他 API 希望只获得一个像句柄这样的文件来读取或写入字节。

A FileInfo 只是给定时间文件状态的快照,文件句柄是您可以操作的 live 文件对象,并且一个路径(如果您的文件系统支持硬链接,则为多个路径)只是最终用户的方便名称。

这些都是非常不同的东西,这就是为什么它们是分开的。您的应用程序可能需要比其他程序员愿意容忍的更复杂的抽象,因此创建自己的抽象,将您需要的所有单独部分结合在一起。例如,考虑以下结构:

import os

type
  AppFileInfo = object
    fileInfo: FileInfo
    file: File
    oneOfMany: string


proc changeFileExt(appFileInfo: AppFileInfo, ext: string): string =
  changeFileExt(appFileInfo.oneOfMany, ext)

proc readAll(appFileInfo: AppFileInfo): string =
  readAll(appFileInfo.file)

这些 proc 只是模仿各自的标准库 API,但使用您更复杂的结构作为输入并根据需要对其进行转换。如果您担心由于额外的 proc 调用而无法优化此抽象,您可以使用 a template instead.

但是,如果您遵循这条路线,在某些时候您将不得不问自己,AppFileInfo 对象的 lifetime 是什么:您使用路径?您是从文件句柄创建的吗?访问部分代码中的 file 字段是否安全,或者它是否未正确初始化?当出现问题时,你会 return 出错或抛出异常吗?也许当您开始问自己这些问题时,您会意识到它们是非常特定于应用程序的,并且很难针对每个用例进行概括。因此,如此复杂的对象在语言标准库中没有多大意义。

我自己创建了缺少的解决方案。我基本上使用全局封装的 table 扩展了 File 类型。由于 UFCS,像这样扩展类型在 Nim 中可能是一个有用的习惯用法。

import tables

type FileObject = object
    file : File
    mode : FileMode
    path : string

proc initFileObject(name: string; mode: FileMode; bufsize = -1) : FileObject =
    result.file = open(name, mode, bufsize)
    result.path = name
    result.mode = mode

var g_fileObjects = initTable[File, FileObject]()
template get(this: File) : var FileObject = g_fileObjects[this]

proc openFile*(filepath: string; mode: FileMode = fmRead; bufsize = -1) : File =
    var fileObject = initFileObject(filepath, mode, bufsize)
    result = fileObject.file
    g_fileObjects[result] = fileObject

proc filePath*(this: File) : string {.raises: KeyError.} =
    return this.get.path

proc fileMode*(this: File) : FileMode {.raises: KeyError.} =
    return this.get.mode

from os import tryRemoveFile

proc closeOrDeleteFile[delete = false](this: File) : bool =
    result = g_fileObjects.hasKey(this)
    if result:
        when delete:
            result = this.filepath.tryRemoveFile()
        g_fileObjects.del(this)
        this.close()

proc closeFile*(this: File) : bool = this.closeOrDeleteFile[:false]
proc deleteFile*(this: File) : bool = this.closeOrDeleteFile[:true]

现在你可以写了


var f = openFile("myFile.txt", fmWrite)
var g = openFile("hello.txt", fmWrite)
echo f.filePath
echo f.deleteFile()
g.writeLine(g.filePath)
echo g.closeFile()