FindFirst 和问号
FindFirst and question mark
我需要删除所有名称以 "a" 开头的文件,然后是三个任意字母和“.txt”扩展名,如 "a123.txt"。这是代码:
var
sFileMask: string;
tsrMessage: TSearchRec;
begin
sFileMask := 'c:/a???.txt';
if SysUtils.FindFirst(sFileMask, 0, tsrMessage) = 0 then
begin
repeat
ShowMessage(tsrMessage.Name);
until FindNext(tsrMessage) <> 0;
SysUtils.FindClose(tsrMessage);
end;
end;
一直以为问号代表一个字符,没想到发现这段代码returns "a.txt", "a1.txt" and "a123.txt" 文件名字。有没有一种简单的方法可以修改代码以使其仅查找 "a123.txt" 之类的文件?
此行为符合设计。 Raymond Chen 在这里解释:How did wildcards work in MS-DOS?
您将在命令解释器中看到完全相同的行为。
C:\Desktop>dir a???.txt
Volume in drive C has no label.
Volume Serial Number is 20DA-7FEB
Directory of C:\Desktop
26/06/2016 14:03 6 a.txt
26/06/2016 14:03 6 a1.txt
26/06/2016 14:03 6 a12.txt
26/06/2016 14:03 6 a123.txt
4 File(s) 24 bytes
0 Dir(s) 286,381,445,120 bytes free
无法说服 FindFirstFile
(Windows 上 RTL FindFirst
后面的 API)按您希望的方式行事。您最好的选择是枚举整个目录,并使用您选择的模式匹配算法执行您自己的过滤。
满足您特定需求的最简单解决方案是替换此:
ShowMessage(tsrMessage.Name);
有了这个
if length(tsrMessage.Name)=8 then ShowMessage(tsrMessage.Name);
这将确保文件名的长度恰好是四个字符+句点+扩展名。就像 David 所说的那样,没有办法让 API 进行这种过滤,所以你必须自己做,但在你的特定情况下,没有必要枚举整个目录。您至少可以让 API 执行 可以 执行的过滤,然后在其上执行您自己的过滤。
编辑:如果你需要确保"a"后面的三个字符是数字,你可以这样做:
if (length(tsrMessage.Name)=8) and tsrMessage[2].IsDigit and tsrMessage[3].IsDigit and tsrMessage[4].IsDigit then ShowMessage(tsrMessage.Name);
前提是您使用的是现代编译器(您需要包含 "Characters" 单元)。另请注意,如果您正在编译移动版本,则需要改用索引 [1]、[2] 和 [3],因为它们的字符串索引从 0 开始。
如果您使用的是旧版本,您可以这样做:
function IsDigit(c : char) : boolean;
begin
Result:=(c>='0') and (c<='9')
end;
if (length(tsrMessage.Name)=8) and IsDigit(tsrMessage[2]) and IsDigit(tsrMessage[3]) and IsDigit(tsrMessage[4]) then ShowMessage(tsrMessage.Name);
我需要删除所有名称以 "a" 开头的文件,然后是三个任意字母和“.txt”扩展名,如 "a123.txt"。这是代码:
var
sFileMask: string;
tsrMessage: TSearchRec;
begin
sFileMask := 'c:/a???.txt';
if SysUtils.FindFirst(sFileMask, 0, tsrMessage) = 0 then
begin
repeat
ShowMessage(tsrMessage.Name);
until FindNext(tsrMessage) <> 0;
SysUtils.FindClose(tsrMessage);
end;
end;
一直以为问号代表一个字符,没想到发现这段代码returns "a.txt", "a1.txt" and "a123.txt" 文件名字。有没有一种简单的方法可以修改代码以使其仅查找 "a123.txt" 之类的文件?
此行为符合设计。 Raymond Chen 在这里解释:How did wildcards work in MS-DOS?
您将在命令解释器中看到完全相同的行为。
C:\Desktop>dir a???.txt Volume in drive C has no label. Volume Serial Number is 20DA-7FEB Directory of C:\Desktop 26/06/2016 14:03 6 a.txt 26/06/2016 14:03 6 a1.txt 26/06/2016 14:03 6 a12.txt 26/06/2016 14:03 6 a123.txt 4 File(s) 24 bytes 0 Dir(s) 286,381,445,120 bytes free
无法说服 FindFirstFile
(Windows 上 RTL FindFirst
后面的 API)按您希望的方式行事。您最好的选择是枚举整个目录,并使用您选择的模式匹配算法执行您自己的过滤。
满足您特定需求的最简单解决方案是替换此:
ShowMessage(tsrMessage.Name);
有了这个
if length(tsrMessage.Name)=8 then ShowMessage(tsrMessage.Name);
这将确保文件名的长度恰好是四个字符+句点+扩展名。就像 David 所说的那样,没有办法让 API 进行这种过滤,所以你必须自己做,但在你的特定情况下,没有必要枚举整个目录。您至少可以让 API 执行 可以 执行的过滤,然后在其上执行您自己的过滤。
编辑:如果你需要确保"a"后面的三个字符是数字,你可以这样做:
if (length(tsrMessage.Name)=8) and tsrMessage[2].IsDigit and tsrMessage[3].IsDigit and tsrMessage[4].IsDigit then ShowMessage(tsrMessage.Name);
前提是您使用的是现代编译器(您需要包含 "Characters" 单元)。另请注意,如果您正在编译移动版本,则需要改用索引 [1]、[2] 和 [3],因为它们的字符串索引从 0 开始。
如果您使用的是旧版本,您可以这样做:
function IsDigit(c : char) : boolean;
begin
Result:=(c>='0') and (c<='9')
end;
if (length(tsrMessage.Name)=8) and IsDigit(tsrMessage[2]) and IsDigit(tsrMessage[3]) and IsDigit(tsrMessage[4]) then ShowMessage(tsrMessage.Name);