在 Windows 上处理文件名中带有回车的文件
Posted
技术标签:
【中文标题】在 Windows 上处理文件名中带有回车的文件【英文标题】:Handling files with carriage return in filename on Windows 【发布时间】:2014-02-05 08:23:47 【问题描述】:我有一个外部 USB、NTFS 格式的硬盘驱动器,其中包含许多文件,我最终需要将这些文件复制到 Windows Server 2008 R2 机器上的驱动器上。
驱动器上的文件是由安装在 Solaris 上的驱动器运行的脚本放置在那里的。进行此复制的用户粗心,在 Windows 机器上编辑了他们的复制脚本,导致 shell 脚本行如下:
cp /sourceDir/sourceFileName /externalDrivePath/targetFileName\r\n
因此,外部驱动器上的文件的文件名中有一个尾随回车符。标准 Windows 复制实用程序(copy、xcopy、robocopy)无法复制这些文件,并出现错误 0x7B / 123:“文件名、目录名或卷标语法不正确。”
我已经测试过,并且相当确定如果我将驱动器再次安装在 Linux 机器上,我应该能够使用以下命令修复文件:
mv /externalDrive/targetFileName\r /externalDrivePath/targetFileName\n
但是,我无法立即访问 Linux 机器。
到目前为止我已经尝试过修复/移动这些文件:
Windows Server 2008 R2 上的“应用”解决方案:
-
在 Windows 资源管理器中重命名文件 - 由于文件数量庞大,这是不可行的解决方案,但无论如何它都不起作用。
通配符模式匹配来自 cmd 提示符的文件名,例如
copy E:\externalDrivePath\targetFileName* anotherPath
。失败并出现 0x7B 错误。
使用 8.3(短)文件名从 cmd 提示符复制文件。根据dir /x
的输出,有问题的文件没有短名称
Windows Server 2008 R2 上的“编程”解决方案:
-
使用 Python/Java 复制/重命名文件:任何打开/复制回车文件的尝试都会引发异常跟踪到相同的 0x7B Windows 错误。
使用 Windows C 'CopyFile' API 复制文件:失败并出现 0x7B 错误。在这里,我使用 FindNextFile API 找到了文件,并将该源路径传递给 CopyFile,但操作系统仍然无法复制文件。
使用 fopen、ofstream 等在 C 中编写我自己的文件复制函数。fopen 调用再次失败,出现 0x7B。
使用 C++ boost::filesystem API 复制文件:失败并出现 0x7B 错误。同样,使用 boost::filesystem::directory_iterator 找到文件并将找到的文件的路径传递给 boost::filesystem::copy_file()
为 Win32 API CopyFile / MoveFile 提供文件路径为“\?\E:\externalDrivePath\targetFileName\r”。调用再次失败,出现 0x7B 错误。
我还尝试将这个驱动器安装在 OS X 机器上以运行副本,希望它能像 Solaris 一样提供对 NTFS 驱动器的支持。但是,它无法将类似的错误消息复制到 Windows——我猜 OS X 的 NTFS 实现更“类似于 Windows”?
如果这在 Windows 上可以解决,我觉得它要么需要一个非常低级的 C 函数来操作 FILE 本身,而不是根据其字符串文件名“打开”它。不知道该怎么做。那个,或者我不知道的一些文件修复实用程序已经包含了这个功能。
对于如何实现我所描述的内容的任何替代方法或建议将不胜感激。
【问题讨论】:
您是否尝试过使用\\?\E:\externalDrivePath\targetFileName
作为 Win32 API 的路径?
文件也有 8.3 的名称吗?从技术上讲,你是可选的,但它很常见。 FindFirstFile
会举报。
将这两个都添加到我现在尝试过的事情列表中,不幸的是两者都不起作用/适用。
【参考方案1】:
TLDR:尝试 CreateFileW
使用前缀为 \\?\
并包含尾随回车符的 unicode 路径。
\\?\
路径语法绕过了许多常见的验证规则、unicode exansion 等,并允许长文件路径,甚至(危险地)允许文件名中包含斜杠等字符。
鉴于此,我想回车应该相当容易处理......
This page relating to long filenames 有更多详细信息。下面引用的相关部分
无需对路径和文件名字符串执行任何 Unicode 规范化以供 Windows 文件 I/O API 函数使用,因为文件系统将路径和文件名视为不透明的 WCHAR 序列。您的应用程序所需的任何规范化都应牢记这一点,而不是对相关 Windows 文件 I/O API 函数的任何调用。
使用API创建目录时,指定路径不能太长,不能附加8.3文件名(即目录名不能超过MAX_PATH减12)。 shell 和文件系统有不同的要求。 可以使用 Windows API 创建一个 shell 用户界面无法正确解释的路径。
来自here
在较新的文件系统上,例如 NTFS、ex-FAT、UDFS 和 FAT32,Windows 以 Unicode 格式将长文件名存储在磁盘上,这意味着始终保留原始长文件名。即使长文件名包含扩展字符并且无论在磁盘读取或写入操作期间处于活动状态的代码页如何,也是如此。文件名的大小写被保留,即使文件系统不区分大小写...
【讨论】:
【参考方案2】:我在 90 年代中期在 Windows 3.11 上遇到过类似的问题。
我最终在 C 程序中使用了 rename
(在 <stdio.h>
中声明)。
如果失败,您可以尝试使用低级 C 系统调用:open
、read
和 write
将文件复制到新名称。
低级调用通常会绕过用户友好的高级函数所施加的限制。
【讨论】:
我预计这将是正确的方法,但令人惊讶的是无法让它工作——open() 和 rename() 都失败了。我还没有深入研究错误原因,但是根据我对 Windows 上 C 的(有限)理解,我认为这两者都依赖于 CreateFile Win32 API。直接在错误文件路径上调用 CreateFile 再次返回 0x7B。以上是关于在 Windows 上处理文件名中带有回车的文件的主要内容,如果未能解决你的问题,请参考以下文章
无法遍历文件名中带有空格的文件、Windows 批处理文件和图像魔法