这是移植到 PHP的 Java 问题的答案。
/**
* @param string $filename
* @return boolean Whether the string is a valid Windows filename.
*/
function isValidWindowsFilename($filename) {
$regex = <<<'EOREGEX'
~ # start of regular expression
^ # Anchor to start of string.
(?! # Assert filename is not: CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])
(\.[^.]*)? # followed by optional extension
$ # and end of string
) # End negative lookahead assertion.
[^<>:"/\\|?*\x00-\x1F]* # Zero or more valid filename chars.
[^<>:"/\\|?*\x00-\x1F\ .] # Last char is not a space or dot.
$ # Anchor to end of string.
#
# tilde = end of regular expression.
# i = pattern modifier PCRE_CASELESS. Make the match case insensitive.
# x = pattern modifier PCRE_EXTENDED. Allows these comments inside the regex.
# D = pattern modifier PCRE_DOLLAR_ENDONLY. A dollar should not match a newline if it is the final character.
~ixD
EOREGEX;
return preg_match($regex, $filename) === 1;
}
请注意,此函数对文件名的长度没有任何限制,但根据平台,实际文件名可能会限制为 260 或 32767 个字符。
这里有一些测试代码来验证它的正确性。
$tests = array(
// Valid filenames
'readme.txt' => true,
'foo.AUX' => true,
'foo.AUX.txt' => true,
'.gitignore' => true, // starting with a period is ok.
// Invalid filenames
'x<y' => false, // less than not allowed.
'x>y' => false, // greater than not allowed.
'q: why not.txt' => false, // colon not allowed.
'He said "hi".doc' => false, // double quote not allowed.
'foo/bar' => false, // Forward slash not allowed.
'foo\\bar' => false, // Backslash not allowed.
'cat readme | backup' => false, // vertical bar (pipe) not allowed.
'ls foo?.rtf' => false, // question mark not allowed
'ls foo*.rtf' => false, // asterisk not allowed
"null\0char" => false, // null character not allowed
'.' => false, // must not end in period
'..' => false, // must not end in period
'period.' => false, // must not end in period
'space ' => false, // must not end with space
// Do not use the following reserved device names for the name of a file:
// CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
// Also avoid these names followed immediately by an extension; for example, NUL.txt is not recommended.
'CON' => false,
'prn.txt' => false,
'LPT9.php' => false,
);
// Disallow characters whose integer representations are in the range from 1 through 31
for ($i = 1; $i <= 31; $i++) {
$tests[chr($i)] = false;
}
?>
<style>
.pass {
background-color: #efe;
}
.fail {
background-color: #fee;
}
</style>
<table>
<thead><tr><th>filename</th><th>expected</th><th>actual</th></tr></thead>
<tbody>
<? foreach ($tests as $filename => $expected) {
$actual = isValidWindowsFilename($filename);
?>
<tr><td><?=htmlentities($filename)?></td><td><?= $expected ? 'valid' : 'invalid' ?></td><td class="<?= $expected === $actual ? 'pass' : 'fail' ?>"><?= $actual ? 'valid' : 'invalid' ?></td></tr>
<?
} ?>
</tbody>
</table>