Подтвердить что ты не робот

Как я могу очистить строку для использования в качестве имени файла?

У меня есть программа, которая преобразует файл в другой формат и сохраняет его. Исходные файлы данных были пронумерованы, но моя подпрограмма выдает имя файла на основе внутреннего имени, найденного в оригинале.

Я попытался запустить пакет в целом каталоге, и он работал нормально, пока я не ударил один файл, чье внутреннее имя имело косую черту. К сожалению! И если он делает это здесь, он может легко сделать это в других файлах. Существует ли какая-либо RTL-процедура (или WinAPI), которая будет дезинфицировать строку и удалить недопустимые символы, чтобы она могла использоваться как имя файла?

4b9b3361

Ответ 1

Вы можете использовать функцию 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 находится в Jedi Windows API под Win32API/JwaShlObj.pas

Ответ 2

Что касается вопроса о том, есть ли какая-либо функция API для дезинфекции файла, имя (или даже проверка его действительности) - кажется, нет. Цитата из комментария Функция PathSearchAndQualify():

Кажется, не существует какого-либо Windows API, который будет проверять путь, введенный пользователем; это остается отдельным упражнением для каждого приложения.

Таким образом, вы можете только проконсультироваться с правилами для подтверждения имени файла из Имена файлов, пути и пространства имен (Windows):

  • Используйте почти любой символ на текущей странице кода для имени, включая символы и символы Юникода в расширенном наборе символов (128-255), за исключением следующего:

    • Запрещены следующие зарезервированные символы:
      < > : "/\ |? *
    • Символы, чьи целые представления находятся в диапазоне от нуля до 31, не допускаются.
    • Любой другой символ, который не разрешает целевая файловая система.
  • Не используйте следующие имена зарезервированных устройств для имени файла: CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9.
    Также избегайте этих имен, за которыми следует сразу расширение; например, NUL.txt не рекомендуется.

Если вы знаете, что ваша программа будет записывать только в файловые системы NTFS, вы, вероятно, можете быть уверены, что нет других символов, которые файловая система не разрешает, поэтому вам нужно будет только проверить, что имя файла не слишком long (используйте константу MAX_PATH) после того, как все недопустимые символы удалены (или заменены символами подчеркивания, например).

Программа также должна удостовериться, что дезинфекция имени файла не привела к конфликтам имен файлов и беззвучно перезаписывает другие файлы, которые в итоге получили одно и то же имя.

Ответ 3

{
  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;

Ответ 4

Проверьте, имеет ли строка недопустимые символы; решение от здесь:

//test if a "fileName" is a valid Windows file name
//Delphi >= 2005 version

function IsValidFileName(const fileName : string) : boolean;
const 
  InvalidCharacters : set of char = ['\', '/', ':', '*', '?', '"', '<', '>', '|'];
var
  c : char;
begin
  result := fileName <> '';

  if result then
  begin
    for c in fileName do
    begin
      result := NOT (c in InvalidCharacters) ;
      if NOT result then break;
    end;
  end;
end; (* IsValidFileName *)

И для строк, возвращающих False, вы можете сделать что-то простое, например this для каждого недопустимого символа:

var
  before, after : string;

begin
  before := 'i am a rogue file/name';

  after  := StringReplace(before, '/', '',
                      [rfReplaceAll, rfIgnoreCase]);
  ShowMessage('Before = '+before);
  ShowMessage('After  = '+after);
end;

// Before = i am a rogue file/name
// After  = i am a rogue filename

Ответ 5

Ну, проще всего использовать регулярное выражение и вашу любимую языковую версию gsub для замены всего, что не является символом слова. Этот класс символов будет "\w" в большинстве языков с Perl-подобными регулярными выражениями или "[A-Za-z0-9]" в качестве простой опции в противном случае.

В частности, в отличие от некоторых примеров в других ответах, вы не хотите искать недопустимые символы для удаления, но ищите допустимые символы для сохранения. Если вы ищете недопустимые символы, вы всегда уязвимы для введения новых символов, но если вы ищете только действительные, вы можете быть немного менее неэффективными (в том, что вы заменили персонажа, которого вы действительно не использовали необходимо), но по крайней мере вы никогда не ошибетесь.

Теперь, если вы хотите сделать новую версию настолько же старой, насколько возможно, вы можете рассмотреть возможность замены. Вместо того, чтобы удалять, вы можете заменить символ или символы, которые, как вы знаете, будут в порядке. Но это довольно интересная проблема, что, вероятно, это хорошая тема для другого вопроса.

Ответ 6

Для тех, кто читает это и хочет использовать PathCleanupSpec, я написал эту тестовую процедуру, которая, похоже, работает... в сети нет определенного количества примеров. Вам нужно включить ShlObj.pas(не уверен, когда был добавлен PathCleanupSpec, но я тестировал это в Delphi 2010) Вам также нужно будет проверить XP sp2 или выше

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;

Ответ 7

Я сделал это:

// Initialized elsewhere...
string folder;
string name;
var prepl = System.IO.Path.GetInvalidPathChars();
var frepl = System.IO.Path.GetInvalidFileNameChars();
foreach (var c in prepl)
{
    folder = folder.Replace(c,'_');
    name = name.Replace(c, '_');
}
foreach (var c in frepl)
{
    folder = folder.Replace(c, '_');
    name = name.Replace(c, '_');
}

Ответ 8

Попробуйте это на современном delphi:

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

Я разрешаю также иметь немецкие умлауты или другие символы, такие как -, _,.. в имени файла.

Ответ 9

// for all platforms (Windows\Unix), uses IOUtils.
function ReplaceInvalidFileNameChars(const aFileName: string; const aReplaceWith: Char = '_'): string;
var
  i: integer;
begin
  Result := aFileName;
  for i := Low(Result) to High(Result) do
    if not TPath.IsValidFileNameChar(Result[i]) then
      Result[i] := aReplaceWith;
  end;
end.