Why does System.IOUtils.TPath.HasValidPathChars accept'?' as a valid char in a path? I set the second parameter (UseWildcards) to false. So, according to the documentation the '?' should be rejected. Still, the function returns True for 'c:\test\test?\'.
UseWildcards = Specifies whether the mask characters are treated as valid path characters (e.g. asterisk or question mark).
Is the behavior of this function only partially correct? Could the function have returned a better result?
The full set of invalid characters can vary by file system. For example, on Windows-based desktop platforms, invalid path characters might include ASCII/Unicode characters 1 through 31, as well as pipe (|) and null (\0).
You can simply use C# inbuilt function " Path. GetInvalidFileNameChars() " to check if there is invalid character in file name and remove it.
Gets an array containing the characters that are not allowed in path names. An array containing the characters that are not allowed in path names. The following example demonstrates the GetInvalidFileNameChars method and the GetInvalidPathChars method to retrieve invalid characters.
On .NET Core 2.1 and later versions: GetFullPath is the only member that throws an ArgumentException if the string contains invalid path characters.
An array containing the characters that are not allowed in path names. The following example demonstrates the GetInvalidFileNameChars method and the GetInvalidPathChars method to retrieve invalid characters.
With few exceptions, the Windows API also limits path names to 260 characters. For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to disable all string parsing and to send the string that follows it straight to the file system.
TPath.HasValidPathChars
is completely broken. This is its implementation:
class function TPath.HasValidPathChars(const Path: string;
const UseWildcards: Boolean): Boolean;
var
PPath: PChar;
PathLen: Integer;
Ch: Char;
I: Integer;
begin
// Result will become True if an invalid path char is found
{$IFDEF MSWINDOWS}
I := GetPosAfterExtendedPrefix(Path) - 1;
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
I := 0;
{$ENDIF POSIX}
PPath := PChar(Path);
PathLen := Length(Path);
Result := False;
while (not Result) and (i < PathLen) do
begin
Ch := PPath[i];
if not IsValidPathChar(Ch) then
if UseWildcards then
if not IsPathWildcardChar(Ch) then
Result := True
else
Inc(i)
else
Result := True
else
Inc(i);
end;
Result := not Result;
end;
The crucial point is the call to IsValidPathChar
. Let's look at what that does.
class function TPath.IsValidPathChar(const AChar: Char): Boolean;
begin
Result := not IsCharInOrderedArray(AChar, FInvalidPathChars);
end;
Now, FInvalidPathChars
is defined to be:
FInvalidPathChars := TCharArray.Create(
#0, #1, #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12,
#13, #14, #15, #16, #17, #18, #19, #20, #21, #22, #23, #24,
#25, #26, #27, #28, #29, #30, #31,
'"', '<', '>', '|'); // DO NOT LOCALIZE;
That is, all ordinals less than 32, and "
, <
, >
and |
.
We also need to understand what IsPathWildcardChar
does.
class function TPath.IsPathWildcardChar(const AChar: Char): Boolean;
begin
Result := IsCharInOrderedArray(AChar, FPathWildcardChars);
end;
Where FPathWildcardChars
is:
FPathWildcardChars := TCharArray.Create('*', '/', ':', '?', '\'); // DO NOT LOCALIZE;
Now, back to TPath.HasValidPathChars
. Let's consider this if
statement:
if not IsValidPathChar(Ch) then
The condition not IsValidPathChar(Ch)
evaluates to True
when IsValidPathChar(Ch)
is False
. Which happens if Ch
is in FInvalidPathChars
. That is if Ch
has ordinal less than 32, or is one of "
, <
, >
and |
.
Your test string is 'C:\test\test?\'
and in fact none of these characters are in FInvalidPathChars
. Which means that the condition in the if not IsValidPathChar(Ch) then
statement always evaluates False
. So even though your string contains a wildcard, it can never reach the subsequent test:
if UseWildcards then
It is easy to conclude that HasValidPathChars
returns the same value irrespective of the value of the input parameter UseWildcards
. And if you have any doubt about the analysis, this program should dispel it:
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.IOUtils;
procedure Main;
var
Ch: Char;
begin
for Ch := low(Ch) to high(Ch) do
if TPath.HasValidPathChars(Ch, False)<>TPath.HasValidPathChars(Ch, True) then
Writeln('different at #' + IntToStr(ord(Ch)));
Writeln('finished');
end;
begin
Main;
Readln;
end.
This looks like yet another function in this dreaded IOUtils
unit that has been improperly implemented and not tested.
I have submitted a bug report: RSP-18696.
Based on having stumbled upon many such problems with IOUtils
, my experience is that the unit is not to be trusted. I would not use it. Find an alternative way to solve your problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With