Você pode chamar Directory.GetFiles () com vários filtros?


353

Estou tentando usar o Directory.GetFiles()método para recuperar uma lista de arquivos de vários tipos, como mp3's e jpg' s. Eu tentei o seguinte sem sorte:

Directory.GetFiles("C:\\path", "*.mp3|*.jpg", SearchOption.AllDirectories);
Directory.GetFiles("C:\\path", "*.mp3;*.jpg", SearchOption.AllDirectories);

Existe uma maneira de fazer isso em uma ligação?


3
Como uma observação lateral, o uso do padrão de pesquisa GetFiles para filtrar a extensão não é seguro. Por exemplo, você tem dois arquivos Test1.xls e Test2.xlsx e deseja filtrar o arquivo xls usando o padrão de pesquisa * .xls, mas GetFiles retorna os dois Test1. .xls e Test2.xlsx. Leia a seção de notas para obter mais informações
kiran

Então, como evitar isso?
Parênteses

2
@kiran Como isso não é seguro? Parece mais um recurso do que um bug.
Kyle Delaney

Respostas:


520

Para .NET 4.0 e posterior,

var files = Directory.EnumerateFiles("C:\\path", "*.*", SearchOption.AllDirectories)
            .Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));

Para versões anteriores do .NET,

var files = Directory.GetFiles("C:\\path", "*.*", SearchOption.AllDirectories)
            .Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));

editar: Por favor, leia os comentários. A melhoria sugerida por Paul Farry e o problema de memória / desempenho apontado por Christian.K são ambos muito importantes.


10
Cara, eu tenho que pensar em termos de LINQ com mais frequência. Ótima solução!
Ken Pespisa

61
Apenas certifique-se de entender as implicações: isso retornará todos os arquivos em uma matriz de strings e depois filtrará pelas extensões especificadas. Isso pode não ser um grande problema se "C: \ Path" não tiver muitos arquivos, mas pode ser um problema de memória / desempenho em "C: \" ou algo assim.
Christian.K

24
... 2 anos depois: código legal, mas cuidado com isso, se você tiver um arquivo que termine com .JPG, ele não será possível. Melhor adicionars.ToLower().Endswith...
Stormenet

107
você pode simplesmente usars.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase)
Paul Farry

119
Note-se que com .NET 4.0, você pode substituir Directory.GetFilescom Directory.EnumerateFiles, msdn.microsoft.com/en-us/library/dd383571.aspx , o que irá evitar os problemas de memória que @ Christian.K menciona.
Jim Mischel

60

Que tal agora:

private static string[] GetFiles(string sourceFolder, string filters, System.IO.SearchOption searchOption)
{
   return filters.Split('|').SelectMany(filter => System.IO.Directory.GetFiles(sourceFolder, filter, searchOption)).ToArray();
}

Encontrei-o aqui (nos comentários): http://msdn.microsoft.com/en-us/library/wz42302f.aspx


Suponho que isso evite as possíveis armadilhas de memória da resposta mais votada? Nesse caso, deve ser avaliado mais alto!
Dan W

11
@ DanW A resposta mais votada certamente sobrecarrega a memória, mas acho que isso não deve ser um problema. Também gostei dessa resposta, mas na verdade é (muito) mais lenta que a resposta aceita. Verifique este SpeedTest
OttO

Obrigado. Fico feliz em ver que é apenas duas vezes mais lento - eu vou ficar com isso por enquanto, eu acho.
Dan W

7
É apenas duas vezes mais lento se houver apenas duas extensões. Se você tiver uma lista de extensões X, será X vezes mais lenta. Porque aqui você está chamando a função Directory.GetFiles várias vezes, enquanto na outra solução ela é chamada apenas uma vez.
Oscar Hermosilla

11
@OscarHermosilla Pode-se usar Parallel.ForEachpara obtê-los em paralelo
FindOutIslamNow

33

Se você tiver uma grande lista de extensões para verificar, use o seguinte. Eu não queria criar muitas instruções OR, então modifiquei o que lette escreveu.

string supportedExtensions = "*.jpg,*.gif,*.png,*.bmp,*.jpe,*.jpeg,*.wmf,*.emf,*.xbm,*.ico,*.eps,*.tif,*.tiff,*.g01,*.g02,*.g03,*.g04,*.g05,*.g06,*.g07,*.g08";
foreach (string imageFile in Directory.GetFiles(_tempDirectory, "*.*", SearchOption.AllDirectories).Where(s => supportedExtensions.Contains(Path.GetExtension(s).ToLower())))
{
    //do work here
}

Quando eu imprimo o imageFile, ele está dando o caminho total dele. Como posso reduzi-lo apenas ao nome do arquivo.
Naresh 24/05

11
System.IO.Path.GetFileName (imageFile)
jnoreiga

Path.GetExtensionretorna '.ext', não '* .ext' (pelo menos em 3.5+).
anulável

2
FYI: Você precisa do System.Linq para
.where

11
Existe uma falha em potencial. Já passamos dos dias em que as extensões precisam ter exatamente três caracteres. Suponha que você possa encontrar um arquivo com .abc, e supportedExtensions, contenha .abcd. Irá corresponder, embora não deva. Para consertar: supportedExtensions = ".jpg|.abcd|";com .Contains(Path.GetExtension(s).ToLower() + "|"). Ou seja, inclua seu caractere separador no teste. IMPORTANTE: seu caractere separador também deve estar após a entrada LAST em supportedExceptions.
Home

31

para

var exts = new[] { "mp3", "jpg" };

Você poderia:

public IEnumerable<string> FilterFiles(string path, params string[] exts) {
    return
        Directory
        .EnumerateFiles(path, "*.*")
        .Where(file => exts.Any(x => file.EndsWith(x, StringComparison.OrdinalIgnoreCase)));
}

Mas o benefício real EnumerateFilesaparece quando você divide os filtros e mescla os resultados:

public IEnumerable<string> FilterFiles(string path, params string[] exts) {
    return 
        exts.Select(x => "*." + x) // turn into globs
        .SelectMany(x => 
            Directory.EnumerateFiles(path, x)
            );
}

Fica um pouco mais rápido se você não precisar transformá-los em globs (ou seja, exts = new[] {"*.mp3", "*.jpg"}já).

Avaliação de desempenho com base no seguinte teste do LinqPad (nota: Perfapenas repete o delegado 10000 vezes) https://gist.github.com/zaus/7454021

(republicado e estendido de 'duplicado', pois essa pergunta não solicitou especificamente LINQ: várias extensões de arquivo searchPattern para System.IO.Directory.GetFiles )


o que você quer dizer com "eu fico um pouco mais rápido se você não precisa transformá-los em globs"? É O (1) ou O (n) (em relação ao número de arquivos, não ao número de extensões)? Eu teria imaginado que é O (1) (ou O (n) em relação ao número de extensões) e provavelmente em algum lugar no intervalo de alguns ciclos de CPU ... Se esse for o caso, provavelmente é - em termos de desempenho - insignificante
BatteryBackupUnit

@BatteryBackupUnit sim, com 10k repetições contra 2 extensões, a diferença glob vs. str é de 3ms, então sim tecnicamente insignificante (veja o link dos resultados de perf), mas sem saber quantas extensões você precisa filtrar, achei que vale a pena ressaltar que existe um diferença; Deixo para você decidir se "uso simplificado" (ie .FilterFiles(path, "jpg", "gif")) é melhor que "globs explícitos" (ie .FilterFiles(path, "*.jpg", "*.gif")).
drzaus

perfeito, obrigado. Desculpe, de alguma forma, pulei o link do github. Talvez eu deva adaptar as configurações de cores da minha tela :) #
22750

Isso suporta extensões maiúsculas, como .JPG ou .MKV?
Wahyu

11
A falha com a solução SelectMany é que ele irá repetir todos os arquivos uma vez por extensão de arquivo passada.
17 of 26

16

Eu sei que é pergunta antiga, mas LINQ: (.NET40 +)

var files = Directory.GetFiles("path_to_files").Where(file => Regex.IsMatch(file, @"^.+\.(wav|mp3|txt)$"));

3
Boa ideia. Considere usar file.ToLower()para combinar facilmente extensões em maiúsculas. E por que não extrair a extensão primeiro, então Regex não tem que examinar caminho inteiro: Regex.IsMatch(Path.GetExtension(file).ToLower(), @"\.(wav|mp3|txt)");
ToolmakerSteve

13

Há também uma solução de descida que parece não ter nenhuma sobrecarga de memória ou desempenho e ser bastante elegante:

string[] filters = new[]{"*.jpg", "*.png", "*.gif"};
string[] filePaths = filters.SelectMany(f => Directory.GetFiles(basePath, f)).ToArray();

11
Suponho que eu possa editá-lo para que ele aceite um número ilimitado de extensões desconhecido com a nova variável de string e uma função Split. Mas mesmo assim, como isso é melhor que a solução da jnoreiga? É mais rápido? Consome menos memória?
Parênteses

11
Há uma troca. Essa abordagem chama GetFiles várias vezes, uma por filtro. Essas várias chamadas podem ser "sobrecarga de desempenho" significativa em algumas situações. Ele tem a vantagem importante de que cada GetFiles retorna apenas uma matriz com os caminhos de arquivo correspondentes . Eu esperava que esse geralmente fosse um bom resultado de desempenho, talvez até um desempenho superior , mas isso precisa ser testado. Se GetFiles for significativamente mais rápido que EnumerateFiles, essa pode ser a melhor abordagem ainda. Observe também que o final ".ToArray ()" pode ser omitido quando IEnumerable é utilizável diretamente.
Home

11

Outra maneira de usar o Linq, mas sem precisar retornar tudo e filtrar isso na memória.

var files = Directory.GetFiles("C:\\path", "*.mp3", SearchOption.AllDirectories).Union(Directory.GetFiles("C:\\path", "*.jpg", SearchOption.AllDirectories));

Na verdade, são 2 chamadas para GetFiles(), mas acho que é consistente com o espírito da pergunta e as retorna em uma enumerável.


Por que usar o Linq, então? Seria mais rápido do que usar uma lista e um intervalo?
ThunderGr

11
não sei o que seria mais rápido e não acho que seja uma pergunta importante. para praticamente qualquer lugar em que você usasse o código para qualquer solução para esse problema, a diferença de desempenho seria insignificante. a questão deve ser o que é mais legível para facilitar a manutenção do código no futuro. Eu acho que essa é uma resposta razoável, porque ela coloca em uma linha de fonte, que eu acho que faz parte do que a pergunta deseja, as chamadas necessárias e expressa claramente a intenção dessa linha. list e addrange estão distraindo várias etapas para realizar a mesma coisa.
Dave Rael

7

Não. Tente o seguinte:

List<string> _searchPatternList = new List<string>();
    ...
    List<string> fileList = new List<string>();
    foreach ( string ext in _searchPatternList )
    {
        foreach ( string subFile in Directory.GetFiles( folderName, ext  )
        {
            fileList.Add( subFile );
        }
    }

    // Sort alpabetically
    fileList.Sort();

    // Add files to the file browser control    
    foreach ( string fileName in fileList )
    {
        ...;
    }

Retirado de: http://blogs.msdn.com/markda/archive/2006/04/20/580075.aspx


7

Deixei

var set = new HashSet<string> { ".mp3", ".jpg" };

Então

Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
         .Where(f => set.Contains(
             new FileInfo(f).Extension,
             StringComparer.OrdinalIgnoreCase));

ou

from file in Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
from ext in set
where String.Equals(ext, new FileInfo(file).Extension, StringComparison.OrdinalIgnoreCase)
select file;

getfiles não tem a sobrecarga que você postou.
Nawfal

5

Não consigo usar o .Wheremétodo porque estou programando no .NET Framework 2.0 (o Linq é suportado apenas no .NET Framework 3.5+).

O código abaixo não diferencia maiúsculas de minúsculas (portanto, .CaBou .cabtambém será listado).

string[] ext = new string[2] { "*.CAB", "*.MSU" };

foreach (string found in ext)
{
    string[] extracted = Directory.GetFiles("C:\\test", found, System.IO.SearchOption.AllDirectories);

    foreach (string file in extracted)
    {
        Console.WriteLine(file);
    }
}

4

A função a seguir pesquisa em vários padrões, separados por vírgulas. Você também pode especificar uma exclusão, por exemplo: "! Web.config" procurará todos os arquivos e excluirá "web.config". Padrões podem ser misturados.

private string[] FindFiles(string directory, string filters, SearchOption searchOption)
{
    if (!Directory.Exists(directory)) return new string[] { };

    var include = (from filter in filters.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) where !string.IsNullOrEmpty(filter.Trim()) select filter.Trim());
    var exclude = (from filter in include where filter.Contains(@"!") select filter);

    include = include.Except(exclude);

    if (include.Count() == 0) include = new string[] { "*" };

    var rxfilters = from filter in exclude select string.Format("^{0}$", filter.Replace("!", "").Replace(".", @"\.").Replace("*", ".*").Replace("?", "."));
    Regex regex = new Regex(string.Join("|", rxfilters.ToArray()));

    List<Thread> workers = new List<Thread>();
    List<string> files = new List<string>();

    foreach (string filter in include)
    {
        Thread worker = new Thread(
            new ThreadStart(
                delegate
                {
                    string[] allfiles = Directory.GetFiles(directory, filter, searchOption);
                    if (exclude.Count() > 0)
                    {
                        lock (files)
                            files.AddRange(allfiles.Where(p => !regex.Match(p).Success));
                    }
                    else
                    {
                        lock (files)
                            files.AddRange(allfiles);
                    }
                }
            ));

        workers.Add(worker);

        worker.Start();
    }

    foreach (Thread worker in workers)
    {
        worker.Join();
    }

    return files.ToArray();

}

Uso:

foreach (string file in FindFiles(@"D:\628.2.11", @"!*.config, !*.js", SearchOption.AllDirectories))
            {
                Console.WriteLine(file);
            }

4
List<string> FileList = new List<string>();
DirectoryInfo di = new DirectoryInfo("C:\\DirName");

IEnumerable<FileInfo> fileList = di.GetFiles("*.*");

//Create the query
IEnumerable<FileInfo> fileQuery = from file in fileList
                                  where (file.Extension.ToLower() == ".jpg" || file.Extension.ToLower() == ".png")
                                  orderby file.LastWriteTime
                                  select file;

foreach (System.IO.FileInfo fi in fileQuery)
{
    fi.Attributes = FileAttributes.Normal;
    FileList.Add(fi.FullName);
}

file.Extension.ToLower()é uma má prática.
23412 abatishchev

então o que devemos usar? @abatishchev
Nitin Sawant

@Nitin:String.Equals(a, b, StringComparison.OrdinalIgnoreCase)
abatishchev

11
Na verdade, file.Extension.Equals (". Jpg", StringComparison.OrdinalIgnoreCase) é o que eu prefiro. Parece ser mais rápido que .ToLower ou .ToUpper, ou é o que dizem em todos os lugares que procurei. Na verdade, .Equals também é mais rápido que ==, pois == chama .Equals e verifica se há nulo (porque você não pode fazer null.Equals (nulo)).
ThunderGr

4

no .NET 2.0 (sem Linq):

public static List<string> GetFilez(string path, System.IO.SearchOption opt,  params string[] patterns)
{
    List<string> filez = new List<string>();
    foreach (string pattern in patterns)
    {
        filez.AddRange(
            System.IO.Directory.GetFiles(path, pattern, opt)
        );
    }


    // filez.Sort(); // Optional
    return filez; // Optional: .ToArray()
}

Então use-o:

foreach (string fn in GetFilez(path
                             , System.IO.SearchOption.AllDirectories
                             , "*.xml", "*.xml.rels", "*.rels"))
{}

4
DirectoryInfo directory = new DirectoryInfo(Server.MapPath("~/Contents/"));

//Using Union

FileInfo[] files = directory.GetFiles("*.xlsx")
                            .Union(directory
                            .GetFiles("*.csv"))
                            .ToArray();

3

Acabei de encontrar uma outra maneira de fazê-lo. Ainda não é uma operação, mas joga fora para ver o que as outras pessoas pensam sobre isso.

private void getFiles(string path)
{
    foreach (string s in Array.FindAll(Directory.GetFiles(path, "*", SearchOption.AllDirectories), predicate_FileMatch))
    {
        Debug.Print(s);
    }
}

private bool predicate_FileMatch(string fileName)
{
    if (fileName.EndsWith(".mp3"))
        return true;
    if (fileName.EndsWith(".jpg"))
        return true;
    return false;
}

3

A respeito

string[] filesPNG = Directory.GetFiles(path, "*.png");
string[] filesJPG = Directory.GetFiles(path, "*.jpg");
string[] filesJPEG = Directory.GetFiles(path, "*.jpeg");

int totalArraySizeAll = filesPNG.Length + filesJPG.Length + filesJPEG.Length;
List<string> filesAll = new List<string>(totalArraySizeAll);
filesAll.AddRange(filesPNG);
filesAll.AddRange(filesJPG);
filesAll.AddRange(filesJPEG);

2

Faça as extensões desejadas em uma string, por exemplo, ".mp3.jpg.wma.wmf" e verifique se cada arquivo contém a extensão desejada. Isso funciona com o .net 2.0, pois não usa o LINQ.

string myExtensions=".jpg.mp3";

string[] files=System.IO.Directory.GetFiles("C:\myfolder");

foreach(string file in files)
{
   if(myExtensions.ToLower().contains(System.IO.Path.GetExtension(s).ToLower()))
   {
      //this file has passed, do something with this file

   }
}

A vantagem dessa abordagem é que você pode adicionar ou remover extensões sem editar o código, ou seja, para adicionar imagens png, basta escrever myExtensions = ". Jpg.mp3.png".


não sabe o que és
Parênteses

2
/// <summary>
/// Returns the names of files in a specified directories that match the specified patterns using LINQ
/// </summary>
/// <param name="srcDirs">The directories to seach</param>
/// <param name="searchPatterns">the list of search patterns</param>
/// <param name="searchOption"></param>
/// <returns>The list of files that match the specified pattern</returns>
public static string[] GetFilesUsingLINQ(string[] srcDirs,
     string[] searchPatterns,
     SearchOption searchOption = SearchOption.AllDirectories)
{
    var r = from dir in srcDirs
            from searchPattern in searchPatterns
            from f in Directory.GetFiles(dir, searchPattern, searchOption)
            select f;

    return r.ToArray();
}

2

Não ... acredito que você precise fazer quantas chamadas os tipos de arquivo desejados.

Eu mesmo criaria uma função pegando uma matriz em seqüências de caracteres com os ramais necessários e, em seguida, iteraria nessa matriz, fazendo todas as chamadas necessárias. Essa função retornaria uma lista genérica dos arquivos correspondentes às extensões que eu enviei.

Espero que ajude.


1

Eu tinha o mesmo problema e não consegui encontrar a solução certa, então escrevi uma função chamada GetFiles:

/// <summary>
/// Get all files with a specific extension
/// </summary>
/// <param name="extensionsToCompare">string list of all the extensions</param>
/// <param name="Location">string of the location</param>
/// <returns>array of all the files with the specific extensions</returns>
public string[] GetFiles(List<string> extensionsToCompare, string Location)
{
    List<string> files = new List<string>();
    foreach (string file in Directory.GetFiles(Location))
    {
        if (extensionsToCompare.Contains(file.Substring(file.IndexOf('.')+1).ToLower())) files.Add(file);
    }
    files.Sort();
    return files.ToArray();
}

Esta função chama Directory.Getfiles()apenas uma vez.

Por exemplo, chame a função assim:

string[] images = GetFiles(new List<string>{"jpg", "png", "gif"}, "imageFolder");

EDIT: Para obter um arquivo com várias extensões, use este:

/// <summary>
    /// Get the file with a specific name and extension
    /// </summary>
    /// <param name="filename">the name of the file to find</param>
    /// <param name="extensionsToCompare">string list of all the extensions</param>
    /// <param name="Location">string of the location</param>
    /// <returns>file with the requested filename</returns>
    public string GetFile( string filename, List<string> extensionsToCompare, string Location)
    {
        foreach (string file in Directory.GetFiles(Location))
        {
            if (extensionsToCompare.Contains(file.Substring(file.IndexOf('.') + 1).ToLower()) &&& file.Substring(Location.Length + 1, (file.IndexOf('.') - (Location.Length + 1))).ToLower() == filename) 
                return file;
        }
        return "";
    }

Por exemplo, chame a função assim:

string image = GetFile("imagename", new List<string>{"jpg", "png", "gif"}, "imageFolder");

1

Eu me pergunto por que existem tantas "soluções" postadas?

Se meu entendimento de novato sobre como o GetFiles funciona estiver correto, existem apenas duas opções e qualquer uma das soluções acima pode ser resumida a estas:

  1. GetFiles e filtro: Rápido, mas um destruidor de memória devido ao armazenamento de sobrecarga até que os filtros sejam aplicados

  2. Filtrar enquanto GetFiles: mais lento, mais filtros são definidos, mas pouco uso de memória, pois nenhuma sobrecarga é armazenada.
    Isso é explicado em uma das postagens acima, com uma referência impressionante: cada opção de filtro causa uma operação GetFile separada, para que a mesma parte do disco rígido seja lida várias vezes.

Na minha opinião, a opção 1) é melhor, mas usar o SearchOption.AllDirectories em pastas como C: \ usaria enormes quantidades de memória.
Portanto, eu faria apenas um sub-método recursivo que percorre todas as subpastas usando a opção 1)

Isso deve causar apenas 1 operação GetFiles em cada pasta e, portanto, ser rápido (Opção 1), mas use apenas uma pequena quantidade de memória, pois os filtros são aplicados após a leitura de cada subpasta -> a sobrecarga é excluída após cada subpasta.

Por favor corrija-me se eu estiver errado. Eu sou como eu disse bastante novo para a programação, mas quero obter uma compreensão mais profunda das coisas para eventualmente se tornar bom nisso :)


1

Se você estiver usando o VB.NET (ou importou a dependência para o seu projeto C #), existe realmente um método de conveniência que permite filtrar várias extensões:

Microsoft.VisualBasic.FileIO.FileSystem.GetFiles("C:\\path", Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, new string[] {"*.mp3", "*.jpg"});

No VB.NET, isso pode ser acessado através do namespace My:

My.Computer.FileSystem.GetFiles("C:\path", FileIO.SearchOption.SearchAllSubDirectories, {"*.mp3", "*.jpg"})

Infelizmente, esses métodos de conveniência não suportam uma variante avaliada preguiçosamente, como o Directory.EnumerateFiles()fazem.


Esta é facilmente a melhor resposta e, no entanto, algo muito mais hacky é aceito. Tenho que amar ASSIM.
Robbie Coyne

0

não sei qual solução é melhor, mas uso isso:

String[] ext = "*.ext1|*.ext2".Split('|');

            List<String> files = new List<String>();
            foreach (String tmp in ext)
            {
                files.AddRange(Directory.GetFiles(dir, tmp, SearchOption.AllDirectories));
            }

0

Aqui está uma maneira simples e elegante de obter arquivos filtrados

var allowedFileExtensions = ".csv,.txt";


var files = Directory.EnumerateFiles(@"C:\MyFolder", "*.*", SearchOption.TopDirectoryOnly)
                .Where(s => allowedFileExtensions.IndexOf(Path.GetExtension(s)) > -1).ToArray(); 

-1

Ou você pode simplesmente converter a sequência de extensões em String ^

vector <string>  extensions = { "*.mp4", "*.avi", "*.flv" };
for (int i = 0; i < extensions.size(); ++i)
{
     String^ ext = gcnew String(extensions[i].c_str());;
     String^ path = "C:\\Users\\Eric\\Videos";
     array<String^>^files = Directory::GetFiles(path,ext);
     Console::WriteLine(ext);
     cout << " " << (files->Length) << endl;
}

2
Isso é c ++, não c #
Parênteses

-1

Usar o padrão de pesquisa GetFiles para filtrar a extensão não é seguro! Por exemplo, você tem dois arquivos Test1.xls e Test2.xlsx e deseja filtrar o arquivo xls usando o padrão de pesquisa * .xls, mas GetFiles retorna Test1.xls e Test2.xlsx. Não sabia disso e obtive um erro na produção ambiente quando alguns arquivos temporários foram repentinamente tratados como arquivos corretos. O padrão de pesquisa era * .txt e os arquivos temporários foram nomeados * .txt20181028_100753898 Portanto, o padrão de pesquisa não é confiável, você também deve adicionar uma verificação extra aos nomes de arquivos.


Não responde a pergunta.
Robbie Coyne
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.