30

ファイルを別の形式に変換して保存するルーチンがあります。元のデータファイルには番号が付けられていましたが、私のルーチンでは、元のファイルにある内部名に基づいてファイル名が出力されます。

ディレクトリ全体でバッチ実行しようとしましたが、内部名にスラッシュが含まれているファイルにヒットするまではうまくいきました。おっとっと!そして、ここでそれを行うと、他のファイルでも簡単に行うことができます. ファイル名として安全に使用できるように、文字列をサニタイズして無効なシンボルを削除する RTL (または WinAPI) ルーチンがどこかにありますか?

4

8 に答える 8

24

PathGetCharType関数PathCleanupSpec関数、または次のトリックを使用できます。

  function IsValidFilePath(const FileName: String): Boolean;
  var
    S: String;
    I: Integer;
  begin
    Result := False;
    S := FileName;
    repeat
      I := LastDelimiter('\/', S);
      MoveFile(nil, PChar(S));
      if (GetLastError = ERROR_ALREADY_EXISTS) or
         (
           (GetFileAttributes(PChar(Copy(S, I + 1, MaxInt))) = INVALID_FILE_ATTRIBUTES)
           and
           (GetLastError=ERROR_INVALID_NAME)
         ) then
        Exit;
      if I>0 then
        S := Copy(S,1,I-1);
    until I = 0;
    Result := True;
  end;

このコードは文字列をパーツに分割し、MoveFileを使用して各パーツを検証します。MoveFileは、無効な文字または予約済みファイル名('COM'など)の場合は失敗し、有効なファイル名の場合は成功またはERROR_ALREADY_EXISTSを返します。


PathCleanupSpecは、Win32API/JwaShlObj.pasの下のJediWindowsAPIにあります。

于 2009-06-07T08:28:01.850 に答える
12

ファイルの名前をサニタイズする (またはその有効性をチェックする) API 関数があるかどうかという質問に関しては、何もないようです。PathSearchAndQualify()関数のコメントからの引用:

ユーザーが入力したパスを検証する Windows API はないようです。これは、各アプリケーションのアドホックな演習として残されています。

そのため、ファイル名、パス、および名前空間 (Windows)からファイル名の有効性の規則のみを参照できます。

  • Unicode 文字および拡張文字セット (128 ~ 255) の文字を含む、現在のコード ページのほぼすべての文字を名前に使用します。ただし、次の文字は除きます。

    • 次の予約文字は使用できません:
      < > : " / \ | ? *
    • 整数表現が 0 ~ 31 の範囲にある文字は使用できません。
    • ターゲット ファイル システムで許可されていないその他の文字。
  • 次の予約済みデバイス名をファイル名に使用しないでください: CONPRNAUXNUL、。 また、これらの名前の直後に拡張子を付けることも避けてください。たとえば、推奨されません。COM1..COM9LPT1..LPT9
    NUL.txt

プログラムが NTFS ファイル システムにのみ書き込みを行うことがわかっている場合は、ファイル システムで許可されていない文字が他にないことをおそらく確認できるので、ファイル名が長すぎないことを確認するだけで済みます (すべての無効な文字が削除された後 (またはアンダースコアなどに置き換えられた後)にMAX_PATH定数)。

また、プログラムは、ファイル名のサニタイズによってファイル名の競合が発生していないことを確認し、同じ名前になった他のファイルを静かに上書きする必要があります。

于 2009-06-07T05:19:04.277 に答える
9
{
  CleanFileName
  ---------------------------------------------------------------------------

  Given an input string strip any chars that would result
  in an invalid file name.  This should just be passed the
  filename not the entire path because the slashes will be
  stripped.  The function ensures that the resulting string
  does not hae multiple spaces together and does not start
  or end with a space.  If the entire string is removed the
  result would not be a valid file name so an error is raised.

}

function CleanFileName(const InputString: string): string;
var
  i: integer;
  ResultWithSpaces: string;
begin

  ResultWithSpaces := InputString;

  for i := 1 to Length(ResultWithSpaces) do
  begin
    // These chars are invalid in file names.
    case ResultWithSpaces[i] of 
      '/', '\', ':', '*', '?', '"', '<', '>', '|', ' ', #$D, #$A, #9:
        // Use a * to indicate a duplicate space so we can remove
        // them at the end.
        {$WARNINGS OFF} // W1047 Unsafe code 'String index to var param'
        if (i > 1) and
          ((ResultWithSpaces[i - 1] = ' ') or (ResultWithSpaces[i - 1] = '*')) then
          ResultWithSpaces[i] := '*'
        else
          ResultWithSpaces[i] := ' ';

        {$WARNINGS ON}
    end;
  end;

  // A * indicates duplicate spaces.  Remove them.
  result := ReplaceStr(ResultWithSpaces, '*', '');

  // Also trim any leading or trailing spaces
  result := Trim(Result);

  if result = '' then
  begin
    raise(Exception.Create('Resulting FileName was empty Input string was: '
      + InputString));
  end;
end;
于 2009-06-14T15:33:15.497 に答える
4

これを読んでPathCleanupSpecを使用したい人のために、私はこのテストルーチンを作成しました。これは機能しているようです...ネット上には例が明らかに不足しています。ShlObj.pasを含める必要があります(PathCleanupSpecがいつ追加されたかはわかりませんが、Delphi 2010でテストしました)XPsp2以降も確認する必要があります

procedure TMainForm.btnTestClick(Sender: TObject);
var
  Path: array [0..MAX_PATH - 1] of WideChar;
  Filename: array[0..MAX_PATH - 1] of WideChar;
  ReturnValue: integer;
  DebugString: string;

begin
  StringToWideChar('a*dodgy%\filename.$&^abc',FileName, MAX_PATH);
  StringToWideChar('C:\',Path, MAX_PATH);
  ReturnValue:= PathCleanupSpec(Path,Filename);
  DebugString:= ('Cleaned up filename:'+Filename+#13+#10);
  if (ReturnValue and $80000000)=$80000000 then
    DebugString:= DebugString+'Fatal result. The cleaned path is not a valid file name'+#13+#10;
  if (ReturnValue and $00000001)=$00000001 then
    DebugString:= DebugString+'Replaced one or more invalid characters'+#13+#10;
  if (ReturnValue and $00000002)=$00000002 then
    DebugString:= DebugString+'Removed one or more invalid characters'+#13+#10;
  if (ReturnValue and $00000004)=$00000004 then
    DebugString:= DebugString+'The returned path is truncated'+#13+#10;
  if (ReturnValue and $00000008)=$00000008 then
    DebugString:= DebugString+'The input path specified at pszDir is too long to allow the formation of a valid file name from pszSpec'+#13;
  ShowMessage(DebugString);
end;
于 2012-06-13T10:01:07.130 に答える
2

簡単なのは、正規表現とお気に入りの言語の のバージョンを使用してgsub、「単語の文字」以外のものを置き換えることです。この文字クラスは\w、Perl のような正規表現を使用するほとんどの言語では " " で[A-Za-z0-9]あり、それ以外の場合は単純なオプションとして " " です。

特に、他の回答のいくつかの例とは対照的に、削除する無効な文字を探すのではなく、保持する有効な文字を探します。無効な文字を探している場合は、常に新しい文字の導入に対して脆弱ですが、有効な文字のみを探している場合は、効率がわずかに低下する可能性があります (実際には使用していない文字を置き換えたという点で)。する必要があります) が、少なくとも間違いはありません。

現在、新しいバージョンをできるだけ古いバージョンに近づけたい場合は、置き換えを検討してください。削除する代わりに、問題ないとわかっている文字を置き換えることができます。しかし、それを行うことは十分に興味深い問題であり、おそらく別の質問の良いトピックです.

于 2009-06-06T23:52:08.577 に答える
0

現代のデルファイでこれを試してください:

 use System.IOUtils;
 ...
 result := TPath.HasValidFileNameChars(FileName, False)

ファイル名にドイツ語のウムラウトや -、_、.. などの他の文字を含めることもできます。

于 2016-11-18T11:42:31.587 に答える