средство очистки строк для имени файла


113

Я ищу функцию php, которая очистит строку и подготовит ее к использованию для имени файла. Кто-нибудь знает удобный?

(Я мог бы написать один, но боюсь, что пропущу персонажа!)

Изменить: для сохранения файлов в файловой системе Windows NTFS.


1
Не могли бы вы уточнить: что будет с умляутами (удалить или преобразовать в базовый символ?) Что произойдет со специальными символами?
Pekka

Для какой файловой системы? Они разные. См. En.wikipedia.org/wiki/…
Гордон

Винда :) Нужно 15 символов.
user151841 07

1
Я хотел бы отметить, что решений «черного списка», предложенных в некоторых ответах, недостаточно, поскольку невозможно проверить все возможные нежелательные символы (помимо специальных символов есть символы с диакритическими знаками и умляутами, целые неанглийские / латинские алфавиты, управляющие символы и т. д.). Поэтому я бы сказал, что подход «белого списка» всегда лучше, а нормализация строки (как это было предложено в комментарии Блэра Макмиллана к ответу Доминика Роджера) позволит естественным образом обрабатывать любые буквы с акцентами, умляутами и т. Д.
Шон Боб

Хороший способ, возможно, использовать регулярные выражения, см. Этот скрипт Python, который я сделал: github.com/gsscoder/normalize-fn
gsscoder

Ответы:


42

Вместо того, чтобы беспокоиться о пропущенных персонажах - как насчет использования белого списка символов, которые вы готовы использовать? Например, вы могли бы позволить только старый добрый a-z, 0-9, _и единственный экземпляр периода ( .). Очевидно, что это больше ограничений, чем у большинства файловых систем, но должно вас обезопасить.


40
Не годится для языков с умляутами. Это приведет к созданию Qubec для Квебека, Dsseldorf для Düsseldorf и так далее.
Pekka

15
Верно - но как я уже сказал: «Например».
Доминик Роджер

5
Что может быть вполне приемлемо для ОП. В противном случае используйте что-нибудь вроде php.net/manual/en/class.normalizer.php
Блэр Макмиллан,

3
На самом деле спрашивали не об этом. Операция запрашивает функцию для очистки строки, а не альтернативу.
i.am.michiel

3
@ i.am.michiel, возможно, но, учитывая, что OP принял это, я предполагаю, что они сочли это полезным.
Доминик Роджер

157

Внеся небольшую корректировку в решение Tor Valamo, чтобы исправить проблему, замеченную Домиником Роджером, вы можете использовать:

// Remove anything which isn't a word, whitespace, number
// or any of the following caracters -_~,;[]().
// If you don't need to handle multi-byte characters
// you can use preg_replace rather than mb_ereg_replace
// Thanks @Łukasz Rysiak!
$file = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $file);
// Remove any runs of periods (thanks falstro!)
$file = mb_ereg_replace("([\.]{2,})", '', $file);

43
Я люблю наркоманов регулярных выражений! -_ ~
AVProgrammer

2
@ iim.hlk - да, отсутствовали закрывающие скобки. Я добавил их сейчас. Спасибо!
Шон Виейра

2
там есть недоработка, вы должны разделить ее на две части и ..потом запустить проверку . Например .?., в конечном итоге будет ... Хотя, поскольку вы фильтруете, /я не вижу, как вы могли бы использовать это дальше прямо сейчас, но это показывает, почему проверка ..здесь неэффективна. Еще лучше, наверное, не заменять, просто отклонить, если он не соответствует требованиям.
falstro

2
Потому что ни одно из этих значений не является недопустимым в файловой системе Windows, и зачем терять больше информации, чем нужно? Вы можете изменить регулярное выражение на простое, [^a-z0-9_-]если хотите, чтобы оно было действительно ограничительным - или просто используйте сгенерированное имя и выбросьте данное имя, чтобы избежать всех этих проблем. :-)
Шон Виейра

3
Обратите внимание, что: является незаконным.
JasonXA

50

Вот как вы можете очистить файловую систему по запросу

function filter_filename($name) {
    // remove illegal file system characters https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
    $name = str_replace(array_merge(
        array_map('chr', range(0, 31)),
        array('<', '>', ':', '"', '/', '\\', '|', '?', '*')
    ), '', $name);
    // maximise filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($name, PATHINFO_EXTENSION);
    $name= mb_strcut(pathinfo($name, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($name)) . ($ext ? '.' . $ext : '');
    return $name;
}

Все остальное разрешено в файловой системе, так что на вопрос есть прекрасный ответ ...

... но может быть опасно разрешать, например, одинарные кавычки 'в имени файла, если вы используете его позже в небезопасном контексте HTML, потому что это абсолютно допустимое имя файла:

 ' onerror= 'alert(document.cookie).jpg

становится дырой XSS :

<img src='<? echo $image ?>' />
// output:
<img src=' ' onerror= 'alert(document.cookie)' />

Из-за этого популярное программное обеспечение CMS Wordpress удаляет их, но они покрывают все соответствующие символы только после некоторых обновлений :

$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
// ... a few rows later are whitespaces removed as well ...
preg_replace( '/[\r\n\t -]+/', '-', $filename )

Наконец, их список теперь включает большинство символов, которые являются частью списка зарезервированных символов URI и небезопасных символов URL .

Конечно, вы можете просто закодировать все эти символы в выводе HTML, но большинство разработчиков и я тоже следуем идиоме «Лучше перестраховаться, чем сожалеть» и заранее удалить их.

Итак, наконец, я предлагаю использовать это:

function filter_filename($filename, $beautify=true) {
    // sanitize filename
    $filename = preg_replace(
        '~
        [<>:"/\\|?*]|            # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
        [\x00-\x1F]|             # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
        [\x7F\xA0\xAD]|          # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
        [#\[\]@!$&\'()+,;=]|     # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
        [{}^\~`]                 # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
        ~x',
        '-', $filename);
    // avoids ".", ".." or ".hiddenFiles"
    $filename = ltrim($filename, '.-');
    // optional beautification
    if ($beautify) $filename = beautify_filename($filename);
    // maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : '');
    return $filename;
}

Все остальное, что не вызывает проблем с файловой системой, должно быть частью дополнительной функции:

function beautify_filename($filename) {
    // reduce consecutive characters
    $filename = preg_replace(array(
        // "file   name.zip" becomes "file-name.zip"
        '/ +/',
        // "file___name.zip" becomes "file-name.zip"
        '/_+/',
        // "file---name.zip" becomes "file-name.zip"
        '/-+/'
    ), '-', $filename);
    $filename = preg_replace(array(
        // "file--.--.-.--name.zip" becomes "file.name.zip"
        '/-*\.-*/',
        // "file...name..zip" becomes "file.name.zip"
        '/\.{2,}/'
    ), '.', $filename);
    // lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
    $filename = mb_strtolower($filename, mb_detect_encoding($filename));
    // ".file-name.-" becomes "file-name"
    $filename = trim($filename, '.-');
    return $filename;
}

И на этом этапе вам нужно сгенерировать имя файла, если результат пуст, и вы можете решить, хотите ли вы кодировать символы UTF-8. Но вам это не нужно, поскольку UTF-8 разрешен во всех файловых системах, которые используются в контекстах веб-хостинга.

Единственное, что вам нужно сделать, это использовать urlencode()(как вы надеетесь сделать это со всеми своими URL-адресами), чтобы имя файла საბეჭდი_მანქანა.jpgстало этим URL-адресом как ваш <img src>или <a href>: http://www.maxrev.de/html/img/%E1%83% A1% E1% 83% 90% E1% 83% 91% E1% 83% 94% E1% 83% AD% E1% 83% 93% E1% 83% 98_% E1% 83% 9B% E1% 83% 90% E1% 83% 9C% E1% 83% A5% E1% 83% 90% E1% 83% 9C% E1% 83% 90.jpg

Stackoverflow делает это, поэтому я могу опубликовать эту ссылку, как это сделал бы пользователь:
http://www.maxrev.de/html/img/ საბეჭდი_მანქანა. Jpg

Так что это полное законное имя файла и не проблема, как @ SequenceDigitale.com упомянул в своем ответе .


3
Хорошая работа. Самый полезный для меня ответ. +1

О ... Функция работает хорошо, но с некоторого времени она начала ставить - между каждым персонажем, вроде, r-u-l-e-sи я понятия не имею, почему это происходит. Конечно, это не ошибка функции, а просто вопрос - в чем может быть причина такого поведения? Неправильная кодировка?

1
Ну да ладно ... Просто сделал отладки , и это происходит только после preg_replaceдюйма filter_filename().

После удаления этих комментариев он снова заработал.

Какие комментарии вы удалили? Если это будет проще, пришлите
mgutt

43

А как насчет использования rawurlencode ()? http://www.php.net/manual/en/function.rawurlencode.php

Вот функция, которая дезинфицирует даже китайские символы:

public static function normalizeString ($str = '')
{
    $str = strip_tags($str); 
    $str = preg_replace('/[\r\n\t ]+/', ' ', $str);
    $str = preg_replace('/[\"\*\/\:\<\>\?\'\|]+/', ' ', $str);
    $str = strtolower($str);
    $str = html_entity_decode( $str, ENT_QUOTES, "utf-8" );
    $str = htmlentities($str, ENT_QUOTES, "utf-8");
    $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
    $str = str_replace(' ', '-', $str);
    $str = rawurlencode($str);
    $str = str_replace('%', '-', $str);
    return $str;
}

Вот объяснение

  1. Убрать HTML-теги
  2. Удалить разрыв / табуляции / возврат каретки
  3. Удалить недопустимые символы для папки и имени файла
  4. Поместите строку в нижний регистр
  5. Удалите иностранные акценты, такие как Éàû, преобразовав его в сущности html, а затем удалите код и сохраните букву.
  6. Заменить пробелы тире
  7. Закодируйте специальные символы, которые могут пройти предыдущие шаги и войти в конфликтное имя файла на сервере. напр. "中文 百强 网"
  8. Замените "%" дефисами, чтобы убедиться, что ссылка на файл не будет перезаписана браузером при запросе файла.

Хорошо, какое-то имя файла не будет актуальным, но в большинстве случаев оно будет работать.

напр. Оригинальное название: "საბეჭდი-და-ტიპოგრაფიული. Jpg"

Название выхода: «-E1-83-A1-E1-83-90-E1-83-91-E1-83-94-E1-83-AD-E1-83-93-E1-83-98 - E1- 83-93-E1-83-90 - E1-83-A2-E1-83-98-E1-83-9E-E1-83-9D-E1-83-92-E1-83-A0-E1-83 -90-E1-83-A4-E1-83-98-E1-83-A3-E1-83-9A-E1-83-98.jpg "

Так лучше, чем ошибка 404.

Надеюсь, это было полезно.

Карл.


1
Вы не удаляете NULL и управляющие символы. Из строки следует удалить все символы ASCII от 0 до 32.
Василий Муса

UTF-8 разрешен в файловой системе и разрешен в URL-адресах, так почему он должен выдавать ошибку 404? Единственное , что вам нужно сделать , это для кодирования URL , http://www.maxrev.de/html/img/საბეჭდი_მანქანა.jpgчтобы http://www.maxrev.de/html/img/%E1%83%A1%E1%83%90%E1%83%91%E1%83%94%E1%83%AD%E1%83%93%E1%83%98_%E1%83%9B%E1%83%90%E1%83%9C%E1%83%A5%E1%83%90%E1%83%9C%E1%83%90.jpgв исходном HTML кода , как вы , надеюсь , делать со всей своей URL.
mgutt

1
Некоторые другие моменты: вы удаляете HTML-теги strip_tags()и после этого удаляете [<>]. К тому же strip_tags()это вообще не нужно. То же самое и с цитатами. При декодировании с помощью кавычек не остается ENT_QUOTES. И str_replace()не удаляет последовательные пробелы, а затем вы используете strtolower()для многобайтовой строки. А зачем вообще переводить в нижний регистр? И, наконец, вы не поймали ни одного зарезервированного символа, как упомянул @BasilMusa. Подробнее в моем ответе: stackoverflow.com/a/42058764/318765
mgutt

влюбился в нее!
Яш Кумар Верма

40

РЕШЕНИЕ 1 - просто и эффективно

$file_name = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $url ) );

  • strtolower () гарантирует, что имя файла будет в нижнем регистре (поскольку регистр не имеет значения внутри URL-адреса, а в имени файла NTFS)
  • [^a-z0-9]+ гарантирует, что имя файла содержит только буквы и цифры
  • Замените недопустимые символы '-'на, чтобы имя файла оставалось читаемым

Пример:

URL:  http://stackoverflow.com/questions/2021624/string-sanitizer-for-filename
File: http-stackoverflow-com-questions-2021624-string-sanitizer-for-filename

РЕШЕНИЕ 2 - для очень длинных URL

Вы хотите кэшировать содержимое URL-адреса и просто должны иметь уникальные имена файлов. Я бы использовал эту функцию:

$file_name = md5( strtolower( $url ) )

это создаст имя файла с фиксированной длиной. Хеш MD5 в большинстве случаев достаточно уникален для такого использования.

Пример:

URL:  https://www.amazon.com/Interstellar-Matthew-McConaughey/dp/B00TU9UFTS/ref=s9_nwrsa_gw_g318_i10_r?_encoding=UTF8&fpl=fresh&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=desktop-1&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_t=36701&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_i=desktop
File: 51301f3edb513f6543779c3a5433b01c

4
Может быть, MD5 может быть проблемой: будьте осторожны при использовании хэшей с URL-адресами. Хотя квадратный корень из числа skrenta.com/2007/08/md5_tutorial.html URL-адресов все еще намного больше, чем текущий размер веб-сайта, если вы столкнетесь с конфликтом, вы получите страницы о Бритни Спирс, когда ожидали страницы о Bugzilla. Вероятно, в нашем случае это не проблема, но для миллиардов страниц я бы выбрал гораздо более крупный алгоритм хеширования, такой как SHA 256, или вообще отказался от него. Источник: boyter.org/2013/01/code-for-a-search-engine-in-php-part-1
Адильбо

15

Что ж, tempnam () сделает это за вас.

http://us2.php.net/manual/en/function.tempnam.php

но это создает совершенно новое имя.

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

$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);

13
preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)

Добавьте / удалите больше допустимых символов в зависимости от того, что разрешено в вашей системе.

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


5
Это позволит использовать такие имена файлов, как .., что может быть или не быть проблемой.
Доминик Роджер

@Dom - просто проверьте это отдельно, так как это фиксированное значение.
Тор Валамо

10

PHP предоставляет функцию для преобразования текста в другой формат

filter.filters.sanitize

Как :

echo filter_var(
   "Lorem Ipsum has been the industry's",FILTER_SANITIZE_URL
); 

Цитата LoremIpsumhasbeentheindustry's


1
Хорошо, но косые черты не удаляются, что может быть проблемой: обход каталога.
func0der

7

безопасно: заменить каждую последовательность НЕ «a-zA-Z0-9_-» на тире; добавьте расширение самостоятельно.

$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).'.'.$extension;

1
Вам необходимо добавить расширение файла через ".": $ Name = preg_replace ('/ [^ a-zA-Z0-9 _-] + /', '-', strtolower ($ name)). '.'. ' . $ extension;
Смит

6

Следующее выражение создает красивую, чистую и удобную строку:

/[^a-z0-9\._-]+/gi

Преобразование сегодняшних финансов: биллинг в сегодняшние финансовые счета


так что в имени файла не может быть точки, подчеркивания или чего-то подобного?
Тор Валамо

2
@Jonathan - что с курсивом?
Доминик Роджер

@ Тор, да, прости. Обновлено. @Dominic, просто акцентирую внимание на тексте.
Sampson

Что такое gism? Я получаю сообщение «Предупреждение: preg_replace () [function.preg-replace]: Неизвестный модификатор 'g'»
user151841,

1
@ user151841 Для preg_replaceглобального флага неявно. Таким образом, если используется preg_replace, в g нет необходимости. Когда мы хотим контролировать количество замен, у preg_replace есть limitпараметр для этого. Прочтите документацию preg_replace, чтобы узнать больше.
rineez

6

Внеся небольшую поправку в решение Шона Виейры, чтобы учесть отдельные точки, вы можете использовать:

preg_replace("([^\w\s\d\.\-_~,;:\[\]\(\)]|[\.]{2,})", '', $file)

2

Они могут быть немного тяжелыми, но они достаточно гибкие, чтобы преобразовать любую строку в «безопасный» enстиль имени файла или имени папки (или, черт возьми, даже очистить ярлыки и тому подобное, если вы его согнете).

1) Создание полного имени файла (с резервным именем в случае, если ввод полностью усечен):

str_file($raw_string, $word_separator, $file_extension, $fallback_name, $length);

2) Или используя только утилиту фильтра без создания полного имени файла (строгий режим trueне позволяет использовать [] или () в имени файла):

str_file_filter($string, $separator, $strict, $length);

3) А вот эти функции:

// Returns filesystem-safe string after cleaning, filtering, and trimming input
function str_file_filter(
    $str,
    $sep = '_',
    $strict = false,
    $trim = 248) {

    $str = strip_tags(htmlspecialchars_decode(strtolower($str))); // lowercase -> decode -> strip tags
    $str = str_replace("%20", ' ', $str); // convert rogue %20s into spaces
    $str = preg_replace("/%[a-z0-9]{1,2}/i", '', $str); // remove hexy things
    $str = str_replace("&nbsp;", ' ', $str); // convert all nbsp into space
    $str = preg_replace("/&#?[a-z0-9]{2,8};/i", '', $str); // remove the other non-tag things
    $str = preg_replace("/\s+/", $sep, $str); // filter multiple spaces
    $str = preg_replace("/\.+/", '.', $str); // filter multiple periods
    $str = preg_replace("/^\.+/", '', $str); // trim leading period

    if ($strict) {
        $str = preg_replace("/([^\w\d\\" . $sep . ".])/", '', $str); // only allow words and digits
    } else {
        $str = preg_replace("/([^\w\d\\" . $sep . "\[\]\(\).])/", '', $str); // allow words, digits, [], and ()
    }

    $str = preg_replace("/\\" . $sep . "+/", $sep, $str); // filter multiple separators
    $str = substr($str, 0, $trim); // trim filename to desired length, note 255 char limit on windows

    return $str;
}


// Returns full file name including fallback and extension
function str_file(
    $str,
    $sep = '_',
    $ext = '',
    $default = '',
    $trim = 248) {

    // Run $str and/or $ext through filters to clean up strings
    $str = str_file_filter($str, $sep);
    $ext = '.' . str_file_filter($ext, '', true);

    // Default file name in case all chars are trimmed from $str, then ensure there is an id at tail
    if (empty($str) && empty($default)) {
        $str = 'no_name__' . date('Y-m-d_H-m_A') . '__' . uniqid();
    } elseif (empty($str)) {
        $str = $default;
    }

    // Return completed string
    if (!empty($ext)) {
        return $str . $ext;
    } else {
        return $str;
    }
}

Итак, допустим, пользовательский ввод: .....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული

И мы хотим преобразовать его во что-то более дружелюбное, чтобы создать tar.gz с длиной имени файла 255 символов. Вот пример использования. Примечание: этот пример включает в себя искаженное расширение tar.gz в качестве доказательства концепции, вы все равно должны фильтровать расширение после того, как строка будет построена по вашему белому списку (ам).

$raw_str = '.....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name  %20   %20 %21 %2C Décor  \/.  /. .  z \... y \...... x ./  “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული';
$fallback_str = 'generated_' . date('Y-m-d_H-m_A');
$bad_extension = '....t&+++a()r.gz[]';

echo str_file($raw_str, '_', $bad_extension, $fallback_str);

Результатом будет: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz

Вы можете поиграть с ним здесь: https://3v4l.org/iSgi8

Или Gist: https://gist.github.com/dhaupin/b109d3a8464239b7754a

РЕДАКТИРОВАТЬ: обновленный фильтр сценария &nbsp;вместо пробела, обновленная ссылка 3v4l


1

Лучшее, что я знаю сегодня, - это статический метод Strings :: webalize из Nette framework.

Кстати, это переводит все диакритические знаки в их основные .. š => s ü => u ß => ss и т. Д.

Для имен файлов необходимо добавить точку "." параметру разрешенных символов.

/**
 * Converts to ASCII.
 * @param  string  UTF-8 encoding
 * @return string  ASCII
 */
public static function toAscii($s)
{
    static $transliterator = NULL;
    if ($transliterator === NULL && class_exists('Transliterator', FALSE)) {
        $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
    }

    $s = preg_replace('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s);
    $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
    $s = str_replace(
        array("\xE2\x80\x9E", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x9A", "\xE2\x80\x98", "\xE2\x80\x99", "\xC2\xB0"),
        array("\x03", "\x03", "\x03", "\x02", "\x02", "\x02", "\x04"), $s
    );
    if ($transliterator !== NULL) {
        $s = $transliterator->transliterate($s);
    }
    if (ICONV_IMPL === 'glibc') {
        $s = str_replace(
            array("\xC2\xBB", "\xC2\xAB", "\xE2\x80\xA6", "\xE2\x84\xA2", "\xC2\xA9", "\xC2\xAE"),
            array('>>', '<<', '...', 'TM', '(c)', '(R)'), $s
        );
        $s = @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); // intentionally @
        $s = strtr($s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e"
            . "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3"
            . "\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
            . "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
            . "\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
            'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.');
        $s = preg_replace('#[^\x00-\x7F]++#', '', $s);
    } else {
        $s = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); // intentionally @
    }
    $s = str_replace(array('`', "'", '"', '^', '~', '?'), '', $s);
    return strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
}


/**
 * Converts to web safe characters [a-z0-9-] text.
 * @param  string  UTF-8 encoding
 * @param  string  allowed characters
 * @param  bool
 * @return string
 */
public static function webalize($s, $charlist = NULL, $lower = TRUE)
{
    $s = self::toAscii($s);
    if ($lower) {
        $s = strtolower($s);
    }
    $s = preg_replace('#[^a-z0-9' . preg_quote($charlist, '#') . ']+#i', '-', $s);
    $s = trim($s, '-');
    return $s;
}

Почему вы хотите заменить диакритические знаки? Просто используйте, urlencode()прежде чем использовать имя файла как srcили href. Единственная используемая в настоящее время файловая система, которая имеет проблемы с UTF-8, - это FATx (используется XBOX): en.wikipedia.org/wiki/Comparison_of_file_systems#Limits И я не думаю, что это используется веб-серверами
mgutt

1

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

Это имеет дополнительное преимущество - надежность переносимости, поскольку все (я почти уверен) операционные системы будут жаловаться, если имя файла неправильно сформировано для этой ОС.

Если это это возможно сделать гнусные вещи с именем файла, возможно , существуют меры , которые могут быть применены перед тестированием файла на резидентной операционной системы - меры , менее сложные , чем полный «санитария» в имени файла.


0

в одну сторону

$bad='/[\/:*?"<>|]/';
$string = 'fi?le*';

function sanitize($str,$pat)
{
    return preg_replace($pat,"",$str);

}
echo sanitize($string,$bad);

А как насчет непечатаемых символов? В этом случае лучше использовать метод белого списка, чем метод черного списка. В основном разрешены только печатаемые имена файлов ASCII, за исключением, конечно, специальных букв. Но для неанглийских языков это еще одна проблема.
TheRealChx101

0

/и ..в указанном пользователем имени файла может быть вредным. Так что избавиться от них нужно примерно так:

$fname = str_replace('..', '', $fname);
$fname = str_replace('/',  '', $fname);

Этого недостаточно! Например, имя файла "./.name" все равно будет выходить за пределы текущего каталога. (Удаление .. здесь ничего не делает, но удаление / превратит ./. В .. и, следовательно, вырвется из целевого каталога.)
cemper93 08

3
@ cemper93 Нет, этот ответ просто превратит строку, в ..nameкоторую ни из чего не вырвется. Удаление всех символов разделителей пути должно быть достаточным для предотвращения любого обхода каталога. (Удаление ..технически не
требуется

@cdhowie Да, но имя файла ./.становится ... И, наконец, в этом ответе отсутствуют все другие зарезервированные символы файловой системы, такие как NULL. Подробнее в моем ответе: stackoverflow.com/a/42058764/318765
mgutt

-4

$ fname = str_replace ('/', '', $ fname);

Поскольку пользователи могут использовать косую черту для разделения двух слов, было бы лучше заменить дефисом вместо NULL.


Где сказано, что он заменит на NULL? Кроме того, это не обрабатывает все специальные символы.
Трэвис Пессетто

Ага - есть и другие специальные символы, которые тоже требуют обработки. В любом случае str_replace здесь не будет лучшей ставкой.
Мартин Ковачев
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.