我遇到了一个问题,我无法在名称长度超过220个字符的目录下创建文件.
以下是一个测试脚本,它至少在我的机器上重现了观察到的行为:
use warnings;
use strict;
use Win32::LongPath;
print system ('rmdir /s /q test');
mkdirL('test');
for my $i (200 .. 255) {
my $dir_name = 'test/' . sprintf("%04d", $i) . ('a' x ($i-4));
mkdirL($dir_name);
openL(\my $fh, '>', "$dir_name/" . ('_' x 200) . '.txt') or die "$^E";
print $fh 'Hello!';
# closeL $fh;
}
Run Code Online (Sandbox Code Playgroud)
此脚本将_________.....___.txt
在" 0220aaaa...aaa/
但不在"下创建文件0221aaaa...aaa/
.
是否存在此问题的原因,如何更改脚本以便*.txt
在所有目录中创建文件?
该脚本在运行时不会消失或产生任何错误消息.
这个问题还没有答案为什么Windows中存在260个字符的路径长度限制?正如另一个问题及其答案清楚地表明的那样,此路径长度限制是Windows问题,而不是NTFS问题.NTFS允许长度最多为32K字符的路径.
事实上,我的测试脚本能够创建最多255个字符的目录(正如NTFS规范所预期的那样),当目录名长度超过220个字符时,似乎无法在目录中存储文件.
问题的根源在于您尝试验证文件是否已创建的方法。欲了解更多信息,请阅读下面的详细说明。
以下是事实:
perl
mkdirL
使用或调用时不会出现任何错误openL
。perl
可以使用 . 打开创建的文件并读取内容openL
。因此,问题是由于无论您使用什么工具,要么使用 ANSI 版本的 Windows API 调用,要么指定相对路径,要么两者的组合,因此它们的路径被限制为 260 个字符。
为了测试这一点,我在 下运行了脚本D:\t
。你瞧,GVim 在以下情况下无法打开文件$i = 250
:
D:\t
是四个字符,\test
另外是五个字符。因此,250 + 9 = 259,当您添加另一个 时,就会达到260\
。
shortpathL
尝试这个:
#!/usr/bin/env perl
use strict;
use warnings;
use Win32::LongPath;
`cmd /c rd /s /q test`;
mkdirL('test') or die "$^E";
my $dir_length = 255;
my $dir_name = 'test/'
. sprintf("%04d", $dir_length)
. ('a' x ($dir_length - 4))
;
mkdirL($dir_name) or die "$^E";
my $file_name = "$dir_name/" . ('z' x 200) . '.txt';
printf "% 3d\n", length $file_name;
openL(\my $fh, '>', $file_name) or die "$^E";
print $fh "Hello!\n" or die "$^E";
close $fh or die "$^E";
system 'notepad.exe', shortpathL($file_name);
Run Code Online (Sandbox Code Playgroud)
你会得到:
因此,请为您不能依赖使用 Unicode 接口的任何外部程序提供短路径。
现在我有机会在64 位 Windows 8.1 系统上实际尝试此操作,但我无法复制该问题。
这是我使用的代码:
#!/usr/bin/env perl
use strict;
use warnings;
use Win32::LongPath;
`cmd /c rd /s /q test`;
mkdirL('test')
or die "$^E";
for my $i (200 .. 255) {
my $dir_name = 'test/' . sprintf("%04d", $i) . ('a' x ($i-4));
mkdirL($dir_name) or die "$^E";
my $file_name = "$dir_name/" . ('_' x 200) . '.txt';
printf "% 3d\n", length $file_name;
openL(\my $fh, '>', $file_name)
or die "$^E";
print $fh 'Hello!' or die "$^E";
close $fh or die "$^E";
}
Run Code Online (Sandbox Code Playgroud)
这是输出:
C:\...\Temp> perl tt.pl 410 第411章 第412章 第413章 第414章 第415章 …… 第460章 第461章 第462章 第463章 第464章 第465章
我还附上了一些屏幕截图:
现在,我可以报告资源管理器在导航到任何目录时遇到问题C:\...\Temp\test\0220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
并且GVim无法打开后续目录中的文件。也就是说,system 'c:/.../gvim.exe', $file_name;
从脚本内部发出的结果是
大概 GVim 在命名文件、路径和命名空间中遇到了以下问题
由于不能将
"\\?\"
前缀与相对路径一起使用,因此相对路径始终限制为 MAX_PATH 字符总数。
巧的是,我的目录路径长度%TEMP%
恰好是33个字符。加上 5( 的长度\test
),我们得到 38。加上 221,我们得到259。现在,当您向该字符串添加目录分隔符时,您会遇到260。
这让我想到,您在其下创建的工作目录的完整路径的长度是多少test
?
更好的消息是,perl
能够读回所写的所有内容:
#!/usr/bin/env perl
use strict;
use warnings;
use Win32::LongPath;
`cmd /c rd /s /q test`;
mkdirL('test')
or die "$^E";
for my $i (220 .. 255) {
my $dir_name = 'test/' . sprintf("%04d", $i) . ('a' x ($i-4));
mkdirL($dir_name) or die "$^E";
my $file_name = "$dir_name/" . ('_' x 200) . '.txt';
printf "% 3d\n", length $file_name;
openL(\my $fh, '>', $file_name)
or die "$^E";
print $fh 'Hello!' or die "$^E";
close $fh or die "$^E";
openL(\my $in, '<', $file_name)
or die "$^E";
print <$in>, "\n" or die "$^E";
close $in or die "$^E";
}
Run Code Online (Sandbox Code Playgroud)
输出:
…… 第459章 你好! 第460章 你好! 第461章 你好! 第462章 你好! 第463章 你好! 第464章 你好! 第465章 你好!
因为Win32::LongPath
内部标准化了路径,所以它们遵循“要指定扩展长度路径,使用"\\?\"
前缀”建议,然后使用 Unicode 版本的 API 调用,例如CreateFileW,openL
不会遇到此类问题。
在此函数的 ANSI 版本中,名称仅限于
MAX_PATH
字符。要将此限制扩展到 32,767 个宽字符,请调用该函数的 Unicode 版本并添加"\\?\"
到路径前面。有关详细信息,请参阅命名文件、路径和命名空间。
您如何验证文件是否已正确创建?
另外,请参阅“为什么 Perl 系统调用无法调用内部 Windows 命令? ”以获取解释qx{cmd /c rd /s /q test}