halabella Ответов: 5

Проблема с шаблоном поиска Directory.Get.Files


Directory.GetFiles(dir, "*.exe")

Также будут возвращены имена файлов, такие как "file.exeandsomethingelse". Горячей исправить?
Пожалуйста помочь.
(Пройдитесь по массиву и проверьте каждый файл - это не вариант, это план "Б")

Sergey Alexandrovich Kryukov

Проголосовали 5 за этот вопрос, потому что вы правы.
--СА

5 Ответов

Рейтинг:
44

Abhishek Sur

Каталог.GetFiles фактически внутренне вызывает Win32 native FindNextFile, чтобы получить все файлы, соответствующие шаблону поиска.

Поскольку ваша windows состоит как из длинных, так и из коротких имен файлов (8,3), она будет соответствовать всему после 3 букв в расширении.

Если вы попробуете Dir *.exe в командной строке DOS, то увидите аналогичный вывод.

Я думаю, что вам нужно использовать

var files = Directory.GetFiles(dir, "*.exe").Where(item => item.EndsWith(".exe"));


Nish Nishant

Проголосовали 5. предложили в качестве ответа.

AspDotNetDev

Это все равно будет соответствовать файлу под названием "file.exexe", что было бы неверно. Но хорошее понимание имен файлов DOS.

Nish Nishant

- Вот именно. EndsWith должен проверить наличие ".exe".

Sergey Alexandrovich Kryukov

Так вот, это правильный обходной путь для Windows lame.
Мой 5.
--СА

Henry Minute

Очень хорошо, Абишек! +5

Sergey Alexandrovich Kryukov

Я также добавил улучшение случая в свой последний ответ.
--СА

Sergey Alexandrovich Kryukov

Абхишек,
Большое вам спасибо за то, что вы тогда объяснили мне эту проблему.
Кроме того, ToLower следует добавить туда, где он должен быть полностью правильным-см. Мой новый ответ.
--СА

Рейтинг:
2

DaveyM69

На этот[^] страница, в разделе заметок говорится::

When using the question mark wildcard character, this method returns only files that match the specified file extension. For example, given two files, "file1.txt" and "file1.txtother", in a directory, a search pattern of "file?.txt" returns just the first file, while a search pattern of "file*.txt" returns both files.

Я еще не пробовал, но хотел бы что-то вроде *?.exe сделать это?


Sergey Alexandrovich Kryukov

Нет, *?.exe ничем не отличается от *.exe. "?" означает ровно один (любой) символ, который будет поглощен"*".

DaveyM69

@SAKryukov: Вы правы - я неправильно прочитал документацию.
Этот блог объясняет это лучше, чем MSDN: http://www.simple-talk.com/community/blogs/andras/archive/2008/08/01/65554.aspx
Похоже, что нет другого решения, кроме как проверять каждый возвращенный результат...

Рейтинг:
13

Sergey Alexandrovich Kryukov

[РЕДАКТИРОВАТЬ]

Смотрите мой первоначальный ответ ниже.


Этот вид подстановочных знаков слишком прост.

Неверно, что "file.exeandsomethingelse" не соответствует "*.exe". Этот шаблон верен, если вы просто хотите получить "все имена файлов, заканчивающиеся на '.exe'". А вы пробовали?

Если шаблон был "*.exe*", то да, "file.exeandsomethingelse" будет соответствовать ему. Не путайте с небрежным либеральным отношением к подстановочным знакам в поиске Windows.

Одно замечание, на всякий случай: в файловой системе больше нет такого понятия, как "расширение". Нет, это точно линия в Unix. Точка внутри имени обрабатывается как любой другой символ (она не ставится рядом с разделителем каталогов). (Не путайте его с оболочкой "extension", которая все еще используется в зарегистрированных "типах файлов".)


К сожалению, Галабелла и Нишант были правы, а я ошибался — мои извинения.
Голос "1" за мой ответ был совершенно правильным.
Я только что протестировал его и обнаружил, что он работает так, как документировано Microsoft:


When using the asterisk wildcard character in a searchPattern, such as "*.txt", the matching behavior when the extension is exactly three characters long is different than when the extension is more or less than three characters long. A searchPattern with a file extension of exactly three characters returns files having an extension of three or more characters, where the first three characters match the file extension specified in the searchPattern. A searchPattern with a file extension of one, two, or more than three characters returns only files having extensions of exactly that length that match the file extension specified in the searchPattern. When using the question mark wildcard character, this method returns only files that match the specified file extension. For example, given two files, "file1.txt" and "file1.txtother", in a directory, a search pattern of "file?.txt" returns just the first file, while a search pattern of "file*.txt" returns both files.

В следующем списке показано поведение различных длин для параметра searchPattern:

"*.abc" возвращает файлы с расширением .abc, .abcd, .abcde, .abcdef и так далее.
"*.abcd" возвращает только файлы с расширением .abcd.
"*.abcde" возвращает только файлы с расширением .abcde.
"*.abcdef" возвращает только файлы с расширением .abcdef.


Ниже это странное поведение получает некоторое объяснение:

Примечание:
Поскольку этот метод проверяет имена файлов как с форматом имени файла 8.3, так и с форматом длинного имени файла, шаблон поиска, подобный "*1*.txt", может возвращать неожиданные имена файлов. Например, использование шаблона поиска "*1*.txt" возвращает "longfilename.txt" потому что эквивалентный формат имени файла 8.3-это "LONGFI~1.txt".


То, что мы имеем здесь, я бы назвал эпическим провалом Microsoft, это делает этот API не очень пригодным для использования, если нам нужно разумное маскирующее поведение. Его нужно обойти.
Это похоже на один из случаев "это не ошибка, это функция".
Надо сказать, во всех снарядах я использовал маскировочные работы, как и ожидал.

Еще раз прошу прощения за неприятности, вызванные неправильным ответом.

Смотрите также: добавлено ToLower в моем другом ответе.

—СА


Espen Harlinn

5+, Хороший ответ :)

Nish Nishant

Эй Крюков,

Это неверно. Каталог.GetFiles не работает таким образом. *.exe будет соответствовать a.exe а также a.exe2. Это сделано специально и также задокументировано.

Sergey Alexandrovich Kryukov

Неужели? Если вы правы, API-это бесполезное дерьмо. Я хочу проверить это сейчас...
--СА

Nish Nishant

Это не проблема .NET, это поведение Win32 API.

Видишь ли http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/49cbae5b-9c54-432e-9f0e-0fd1f94db287

Sergey Alexandrovich Kryukov

Кто бы сомневался, что это вопрос "должен умереть"?
Уровень новой функциональности .NET всегда лучше; это всего лишь некоторое взаимодействие.
Что забавно, даже старое мерцание в компонентах, созданных в Windows 3*, все еще существует.
Спасибо.
--СА

Sergey Alexandrovich Kryukov

К сожалению, вы с ОПОМ правы. (Я проверил на V. 3.5). И да, это согласно документации.
Это должно умереть!

Извините, я исправлю свой ответ.
--СА

Sergey Alexandrovich Kryukov

Я исправил свой ответ и признал свою ошибку.
--СА

Sergey Alexandrovich Kryukov

К сожалению, кто-то проголосовал "1" абсолютно правильно, Пожалуйста, смотрите мой комментарий.
Я исправлю свой ответ.
Microsoft, к сожалению, работает сейчас как "это должно быть", но "как документально подтверждено".
--СА

fjdiewornncalwe

Не знаю, почему вы получили 1. Отличный и обстоятельный ответ. +5 от меня.

Nish Nishant

Я не голосовал за 1, но ответ неверен.

Sergey Alexandrovich Kryukov

Но голосование "1" было цитатой правильной, см. мое обновление.
--СА

Sergey Alexandrovich Kryukov

Голос "1" является действительным. То, что я сказал, неправда. Я должен это признать (см. Мой комментарий)
--СА

fjdiewornncalwe

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

Sergey Alexandrovich Kryukov

Ну что ж, спасибо. В конце концов, я сам придумал ответ.
--СА

Рейтинг:
12

halabella

Спасибо за обсуждение! Чтобы сделать эту тему полезной для кого-то:

List <string> exes = SearchFilesRecursive(@"d:\", "*.exe");

public List<string> SearchFilesRecursive(string directory, string pattern)
 {
     Stack<string> stack = new Stack<string>();
     List<string> result = new List<string>();
     stack.Push(directory);
     List<string> current_dir = new List<string>();
     int extention_length = pattern.Length - 1;
     while (stack.Count > 0)
     {
         string dir = stack.Pop();
         try
         {
             current_dir.AddRange(Directory.GetFiles(dir, pattern));
             for (int i = 0; i < current_dir.Count; i++)
             {
                 if (current_dir[i].LastIndexOf(".") != current_dir[i].Length - extention_length)
                 {
                     current_dir.RemoveAt(i);
                     i--;
                 }
             }
             result.AddRange(current_dir);
             current_dir.Clear();

             // Add all directories at this directory.
             foreach (string dn in Directory.GetDirectories(dir))
             {
                 stack.Push(dn);
             }
         }
         catch
         {
             // Couldn't open the directory
         }
     }
     return result;
 }


Рейтинг:
1

Sergey Alexandrovich Kryukov

Немного исправим правильный ответ Абхишека Сура:

const string ExtExe = ".exe";
const string MaskExe = "*" + ExtExe;
//..
var files =
   Directory.GetFiles(dir, MaskExe).Where(item => item.ToLower().EndsWith(ExtExe));


Я думаю что это хорошо сделать предикат Where без учета регистра.

—СА


Jonathan Gilbert

Строка.Метод EndsWith имеет перегрузку, которая принимает значение перечисления StringComparison. Таким образом, вместо того, чтобы создавать новую строку для каждого элемента в нижнем регистре, я считаю, что вы можете сэкономить время и отток памяти, как это:

.Где(номенклатура =&ГТ; пункт.EndsWith(ExtExe, StringComparison.InvariantCultureIgnoreCase))

(Это также выводит вычисления культуры из уравнения-поскольку мы знаем, что сравниваем со строкой ".exe", не имеет значения, какова текущая локаль, потому что правила верхнего/нижнего регистра для расширения файла ".exe" в значительной степени гарантируют, что они не будут включать особые случаи, зависящие от культуры. Основная строка.Метод ToLower() основан на чертах характера для текущей локали.)

Sergey Alexandrovich Kryukov

Согласитесь, что это улучшение полезно и наиболее практично, но я считаю, что за кулисами оно занимает почти столько же циклов памяти и процессора. Но его можно измерить...
Спасибо Вам за этот комментарий.
--СА

Jonathan Gilbert

Я только что сравнил его и обнаружил, что в моей системе вариант, который не создает новый строковый объект, почти в два раза быстрее. Для 10 000 000 циклов, каждый из которых содержит 8 различных капитализаций с различными статусами pass/fail:

.EndsWith(StringComparison): 00:00:08.5870000 9,316,408.52 / сек
.ToLower().EndsWith(): 00:00:15.9700000 5,009,392.61 / сек

Sergey Alexandrovich Kryukov

Ну что ж, интересный результат, Спасибо, что поделились. В критических по времени ситуациях это может быть неплохим поводом для улучшения некоторого кода :-)
--СА