You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下C#如何判断路径是否为普通文件?兼容Windows方案

实现与C的S_ISREG等价的C#普通文件判断逻辑(跨平台兼容)

要实现和C语言中stat+S_ISREG完全等价的普通文件判断,同时在Windows上避免File.Exists误判设备节点的问题,需要分平台针对性实现,以下是具体方案:

核心思路

  • Unix-like系统(Linux/macOS/FreeBSD等):直接调用系统stat函数,复用C代码的S_ISREG判断逻辑,确保和原逻辑完全一致。
  • Windows系统:通过Win32 API打开文件并获取文件信息,排除目录和设备节点,避免File.Exists的局限性。
  • 跨平台兼容:运行时判断操作系统,自动切换实现,对未知平台提供降级方案。

代码实现

1. Unix平台实现(直接调用stat)

using System.Runtime.InteropServices;

public static class UnixFileUtils
{
    [StructLayout(LayoutKind.Sequential)]
    private struct Stat
    {
        public ulong st_dev;
        public ulong st_ino;
        [MarshalAs(UnmanagedType.U4)]
        public uint st_mode;
        // 省略其他无关字段,仅保留st_mode即可
    }

    [DllImport("libc", SetLastError = true, CharSet = CharSet.Ansi)]
    private static extern int stat(string path, ref Stat buf);

    // S_IFMT是文件类型掩码,S_IFREG表示普通文件
    private const uint S_IFMT = 0xF000;
    private const uint S_IFREG = 0x8000;

    public static bool IsRegularFile(string path)
    {
        if (string.IsNullOrWhiteSpace(path))
            return false;

        Stat statBuf = new Stat();
        if (stat(path, ref statBuf) != 0)
            return false; // 调用失败(如文件不存在),返回false

        return (statBuf.st_mode & S_IFMT) == S_IFREG;
    }
}

2. Windows平台实现(Win32 API判断)

using System.Runtime.InteropServices;

public static class WindowsFileUtils
{
    [StructLayout(LayoutKind.Sequential)]
    private struct BY_HANDLE_FILE_INFORMATION
    {
        public uint FileAttributes;
        public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
        public uint VolumeSerialNumber;
        public uint FileSizeHigh;
        public uint FileSizeLow;
        public uint NumberOfLinks;
        public uint FileIndexHigh;
        public uint FileIndexLow;
    }

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern IntPtr CreateFile(
        string lpFileName,
        uint dwDesiredAccess,
        uint dwShareMode,
        IntPtr lpSecurityAttributes,
        uint dwCreationDisposition,
        uint dwFlagsAndAttributes,
        IntPtr hTemplateFile);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetFileInformationByHandle(
        IntPtr hFile,
        out BY_HANDLE_FILE_INFORMATION lpFileInformation);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hObject);

    private const uint GENERIC_READ = 0x80000000;
    private const uint FILE_SHARE_READ = 0x00000001;
    private const uint OPEN_EXISTING = 3;
    private const uint FILE_ATTRIBUTE_DEVICE = 0x40; // 设备文件标识位

    public static bool IsRegularFile(string path)
    {
        if (string.IsNullOrWhiteSpace(path))
            return false;

        // 以只读方式打开文件,避免修改
        IntPtr hFile = CreateFile(
            path,
            GENERIC_READ,
            FILE_SHARE_READ,
            IntPtr.Zero,
            OPEN_EXISTING,
            0,
            IntPtr.Zero);

        if (hFile == IntPtr.Zero || hFile == new IntPtr(-1))
            return false; // 无法打开(如文件不存在、权限不足)

        try
        {
            if (!GetFileInformationByHandle(hFile, out var fileInfo))
                return false;

            // 排除目录
            if ((fileInfo.FileAttributes & (uint)FileAttributes.Directory) != 0)
                return false;

            // 排除设备节点(如NUL、CON等)
            return (fileInfo.FileAttributes & FILE_ATTRIBUTE_DEVICE) == 0;
        }
        finally
        {
            CloseHandle(hFile); // 确保句柄释放
        }
    }
}

3. 跨平台封装入口

public static class FileChecker
{
    public static bool IsRegularFile(string path)
    {
        if (OperatingSystem.IsWindows())
        {
            return WindowsFileUtils.IsRegularFile(path);
        }
        else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD())
        {
            return UnixFileUtils.IsRegularFile(path);
        }
        else
        {
            // 未知平台降级处理:结合File.Exists和Directory.Exists做基础判断
            return File.Exists(path) && !Directory.Exists(path);
        }
    }
}

关键说明

  • Unix平台:直接调用stat函数,和原C代码逻辑完全一致,能准确识别普通文件,排除目录、设备节点、符号链接(stat会自动跟随符号链接到目标文件)。
  • Windows平台:通过CreateFile+GetFileInformationByHandle获取真实文件属性,避免File.Exists将NUL、CON等设备节点误判为普通文件。
  • 兼容性:使用.NET 6+提供的OperatingSystem.IsXXX()做运行时平台判断,无需条件编译;未知平台降级到基础判断,保证可用性。

内容的提问来源于stack exchange,提问作者Jonathan Gilbert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 18:53:12