Я получаю строку от внешнего процесса. Я хочу использовать эту строку для создания имени файла, а затем записать в этот файл. Вот мой фрагмент кода для этого:
String s = ... // comes from external source
File currentFile = new File(System.getProperty("user.home"), s);
PrintWriter currentWriter = new PrintWriter(currentFile);
Если s содержит недопустимый символ, такой как '/' в ОС на базе Unix, тогда (справедливо) генерируется исключение java.io.FileNotFoundException.
Как я могу безопасно закодировать строку, чтобы ее можно было использовать в качестве имени файла?
Изменить: я надеюсь на вызов API, который сделает это за меня.
Я могу это сделать:
String s = ... // comes from external source
File currentFile = new File(System.getProperty("user.home"), URLEncoder.encode(s, "UTF-8"));
PrintWriter currentWriter = new PrintWriter(currentFile);
Но я не уверен, надежен ли URLEncoder для этой цели.
Ответы:
Если вы хотите, чтобы результат напоминал исходный файл, SHA-1 или любая другая схема хеширования не подходит. Если необходимо избегать коллизий, то простая замена или удаление «плохих» символов тоже не решение.
Вместо этого вы хотите что-то вроде этого. (Примечание: это следует рассматривать как иллюстративный пример, а не как что-то для копирования и вставки.)
Это решение обеспечивает обратимое кодирование (без конфликтов), при котором закодированные строки в большинстве случаев напоминают исходные строки. Я предполагаю, что вы используете 8-битные символы.
URLEncoder
работает, но имеет тот недостаток, что он кодирует множество допустимых символов имени файла.Если вам нужно решение с не гарантированной обратимостью, просто удалите «плохие» символы, а не заменяйте их управляющими последовательностями.
Обратное кодирование выше должно быть столь же простым для реализации.
источник
Я предлагаю использовать подход «белого списка», то есть не пытайтесь отфильтровать плохие символы. Вместо этого определите, что хорошо. Вы можете либо отклонить имя файла, либо отфильтровать его. Если вы хотите его отфильтровать:
Что это делает заменяет любой символ , который не является числом, буква или подчеркивание ни с чем. В качестве альтернативы вы можете заменить их другим символом (например, подчеркиванием).
Проблема в том, что если это общий каталог, вы не хотите конфликтовать с именами файлов. Даже если пользовательские области хранения разделены пользователем, вы можете получить конфликтующее имя файла, просто отфильтровав плохие символы. Имя, введенное пользователем, часто бывает полезным, если он тоже когда-нибудь захочет его скачать.
По этой причине я обычно позволяю пользователю вводить то, что он хочет, сохранять имя файла на основе выбранной мной схемы (например, userId_fileId), а затем сохранять имя файла пользователя в таблице базы данных. Таким образом, вы можете отображать его обратно пользователю, хранить вещи так, как вы хотите, и не подвергать риску безопасность и не стирать другие файлы.
Вы также можете хэшировать файл (например, MD5-хеш), но тогда вы не можете перечислить файлы, которые пользователь вставил (в любом случае, с осмысленным именем).
РЕДАКТИРОВАТЬ: исправлено регулярное выражение для java
источник
"\\W+"
для регулярного выражения в Java. Обратная косая черта сначала применяется к самой строке и\W
не является допустимой escape-последовательностью. Я попытался отредактировать ответ, но похоже, что кто-то отклонил мою правку :(Это зависит от того, должно ли кодирование быть обратимым или нет.
обратимый
Используйте кодировку URL (
java.net.URLEncoder
) для замены специальных символов на%xx
. Обратите внимание, что вы позаботитесь о специальных случаях, когда строка равна.
, равна..
или пуста! ¹ Многие программы используют кодировку URL-адресов для создания имен файлов, поэтому это стандартный метод, понятный всем.Необратимый
Используйте хэш (например, SHA-1) данной строки. Современные алгоритмы хеширования ( не MD5) можно считать бесконфликтными. Фактически, у вас будет прорыв в криптографии, если вы обнаружите коллизию.
¹ Вы можете элегантно обработать все 3 особых случая, используя префикс, например
"myApp-"
. Если вы поместите файл напрямую$HOME
, вам все равно придется это сделать, чтобы избежать конфликтов с существующими файлами, такими как ".bashrc".источник
Вот что я использую:
Это означает замену каждого символа, который не является буквой, числом, подчеркиванием или точкой, подчеркиванием, используя регулярное выражение.
Это означает, что что-то вроде «Как конвертировать £ в $» станет «How_to_convert___to__». По общему признанию, этот результат не очень удобен для пользователя, но он безопасен, и полученные имена каталогов / файлов гарантированно работают везде. В моем случае результат не отображается пользователю и, следовательно, не является проблемой, но вы можете изменить регулярное выражение, чтобы оно было более разрешительным.
Стоит отметить, что еще одна проблема, с которой я столкнулся, заключалась в том, что я иногда получал идентичные имена (поскольку это основано на вводе пользователя), поэтому вы должны знать об этом, поскольку вы не можете иметь несколько каталогов / файлов с одинаковым именем в одном каталоге. . Я просто добавил текущее время и дату, а также короткую случайную строку, чтобы этого избежать. (фактическая случайная строка, а не хеш имени файла, поскольку идентичные имена файлов приведут к одинаковым хешам)
Кроме того, вам может потребоваться усечь или иным образом сократить результирующую строку, поскольку она может превышать ограничение в 255 символов, которое есть в некоторых системах.
источник
Для тех, кто ищет общее решение, это могут быть общие критерии:
Для этого мы можем использовать регулярное выражение для сопоставления недопустимых символов, кодировать их в процентах , а затем ограничивать длину закодированной строки.
Узоры
Приведенный выше шаблон основан на консервативном подмножестве разрешенных символов в спецификации POSIX .
Если вы хотите разрешить символ точки, используйте:
Только будьте осторожны со строками типа "." и ".."
Если вы хотите избежать конфликтов в файловых системах, нечувствительных к регистру, вам нужно избегать заглавных букв:
Или экранируйте строчные буквы:
Вместо использования белого списка вы можете занести в черный список зарезервированные символы для вашей конкретной файловой системы. EG Это регулярное выражение подходит для файловых систем FAT32:
Длина
На Android безопасным пределом является 127 символов . Многие файловые системы позволяют использовать 255 символов.
Если вы предпочитаете сохранить хвост, а не головку вашей веревки, используйте:
Декодирование
Чтобы преобразовать имя файла обратно в исходную строку, используйте:
Ограничения
Поскольку более длинные строки усекаются, существует вероятность конфликта имен при кодировании или повреждения при декодировании.
источник
Pattern.compile("[^A-Za-z0-9_\\-]")
Попробуйте использовать следующее регулярное выражение, которое заменяет каждый недопустимый символ имени файла пробелом:
источник
_
или-
.Выберите свой яд из вариантов, представленных commons-codec , например:
источник
sha1
;sha
устарела.Вероятно, это не самый эффективный способ, но он показывает, как это сделать с помощью конвейеров Java 8:
Решение можно улучшить, создав собственный сборщик, который использует StringBuilder, поэтому вам не нужно преобразовывать каждый легкий символ в строку с тяжелым весом.
источник
Вы можете удалить недопустимые символы ('/', '\', '?', '*'), А затем использовать его.
источник