Como remover caracteres ilegais do caminho e dos nomes de arquivo?


456

Preciso de uma maneira simples e robusta de remover caracteres ilegais de caminho e arquivo de uma sequência simples. Eu usei o código abaixo, mas ele não parece fazer nada, o que estou perdendo?

using System;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";

            illegal = illegal.Trim(Path.GetInvalidFileNameChars());
            illegal = illegal.Trim(Path.GetInvalidPathChars());

            Console.WriteLine(illegal);
            Console.ReadLine();
        }
    }
}

1
Aparar remove os caracteres do início e do fim das strings. No entanto, você provavelmente deve perguntar por que os dados são inválidos e, em vez de tentar limpar / corrigir os dados, rejeite-os.
User7116

8
Nomes de estilo Unix não são válidos no Windows e eu não quero lidar com nomes curtos 8.3.
Gary Willoughby

GetInvalidFileNameChars()tira coisas como: \ etc dos caminhos das pastas.
CAD cara

1
Path.GetInvalidPathChars()não parece despir *ou?
CAD cara

19
Testei cinco respostas desta pergunta (loop cronometrado de 100.000) e o método a seguir é o mais rápido. A expressão regular ficou em segundo lugar e foi 25% mais lenta: public string GetSafeFilename (string filename) {return string.Join ("_", filename.Split (Path.GetInvalidFileNameChars ())); }
Brain2000 15/07/16

Respostas:


494

Tente algo assim;

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());

foreach (char c in invalid)
{
    illegal = illegal.Replace(c.ToString(), ""); 
}

Mas tenho que concordar com os comentários, provavelmente tentaria lidar com a fonte dos caminhos ilegais, em vez de tentar transformar um caminho ilegal em legítimo, mas provavelmente não intencional.

Edit: ou uma solução potencialmente 'melhor', usando Regex.

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
illegal = r.Replace(illegal, "");

Ainda assim, a pergunta precisa ser feita, por que você está fazendo isso em primeiro lugar.


40
Não é necessário anexar as duas listas. A lista de caracteres de nome de arquivo ilegal contém a lista de caracteres de caminho ilegal e mais alguns. Aqui estão listas de ambas as listas convertidas para int: 34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47 34,60,62,124,0,1,2 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 , 28,29,30,31
Sarel Botha

9
@sjbotha isso pode ser verdade na implementação do .NET da Windows e da Microsoft. Não estou disposto a fazer a mesma suposição para, digamos, o Linux em execução mono.
Matthew Scharley

7
Em relação à primeira solução. Um StringBuilder não deveria ser mais eficiente do que as atribuições de string?
precisa

6
Para o que vale a pena, @MatthewScharley, a implementação Mono de GetInvalidPathChars () retorna apenas 0x00 e GetInvalidFileNameChars () retorna apenas 0x00 e '/' quando executado em plataformas não Windows. No Windows, as listas de caracteres inválidos são muito maiores e GetInvalidPathChars () é totalmente duplicado dentro de GetInvalidFileNameChars (). Isso não vai mudar em um futuro previsível, então tudo o que você está realmente fazendo é dobrar o tempo que essa função leva para ser executada, porque você está preocupado que a definição de um caminho válido mude em breve. O que não vai.
Warren Rumak

13
@ Charles esta discussão é tão desnecessária ... o código deve sempre ser otimizado e não há risco de que isso esteja incorreto. Um nome de arquivo também faz parte do caminho. Portanto, é ilógico que GetInvalidPathChars()poderia conter caracteres que GetInvalidFileNameChars()não. Você não está corrigindo a otimização "prematura". Você está simplesmente usando código incorreto.
Stefan Fabian

355

A pergunta original solicitada para "remover caracteres ilegais":

public string RemoveInvalidChars(string filename)
{
    return string.Concat(filename.Split(Path.GetInvalidFileNameChars()));
}

Você pode substituí-los:

public string ReplaceInvalidChars(string filename)
{
    return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));    
}

Esta resposta foi em outro tópico de Ceres , eu realmente gosto pura e simples.


10
Para responder com precisão à pergunta do OP, você precisaria usar "" em vez de "_", mas sua resposta provavelmente se aplica a mais de nós na prática. Eu acho que substituir caracteres ilegais por algum legal é mais comum.
BH

37
Testei cinco métodos dessa pergunta (loop cronometrado de 100.000) e esse método é o mais rápido. A expressão regular ficou em 2º lugar e foi 25% mais lenta que este método.
Brain2000

10
Para endereço @BH 's comentário, pode-se simplesmente usar String.Concat (name.Split (Path.GetInvalidFileNameChars ()))
Michael Sutton

210

Eu uso o Linq para limpar nomes de arquivos. Você pode facilmente estender isso para verificar também caminhos válidos.

private static string CleanFileName(string fileName)
{
    return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty));
}

Atualizar

Alguns comentários indicam que esse método não está funcionando para eles, por isso incluí um link para um snippet DotNetFiddle para que você possa validar o método.

https://dotnetfiddle.net/nw1SWY


4
Isto não funcionou para mim. O método não está retornando a sequência limpa. Ele está retornando o nome do arquivo passado como está.
21413 Karan

O que @Karan disse, isso não funciona, a string original volta.
31416 Jon

Você pode realmente fazer isso com o Linq como este no entanto: var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). O desempenho provavelmente não é ótimo, mas isso provavelmente não importa.
Casey

2
@Karan ou Jon Que entrada você está enviando esta função? Veja minha edição para verificação deste método.
Michael Minton

3
É fácil - os caras estavam passando strings com caracteres válidos. Promovido para solução agregada legal.
Nickmaovich

89

Você pode remover caracteres ilegais usando o Linq assim:

var invalidChars = Path.GetInvalidFileNameChars();

var invalidCharsRemoved = stringWithInvalidChars
.Where(x => !invalidChars.Contains(x))
.ToArray();

EDIT
É assim que se parece com a edição necessária mencionada nos comentários:

var invalidChars = Path.GetInvalidFileNameChars();

string invalidCharsRemoved = new string(stringWithInvalidChars
  .Where(x => !invalidChars.Contains(x))
  .ToArray());

1
Eu gosto desta maneira: você mantém apenas os caracteres permitidos na string (que nada mais é do que uma matriz de caracteres).
Cara Pascalou

6
Sei que essa é uma pergunta antiga, mas é uma resposta impressionante. No entanto, eu gostaria de acrescentar que em c # você não pode converter de char [] para string de forma implícita ou explícita (louco, eu sei), portanto você precisará colocá-lo em um construtor de string.
JNYRanger #

1
Não confirmei isso, mas espero que Path.GetInvalidPathChars () seja um superconjunto de GetInvalidFileNameChars () e abranja nomes de arquivos e caminhos, portanto, provavelmente eu o usaria.
angularsen 9/01/2015

3
@anjdreas, na verdade, Path.GetInvalidPathChars () parece ser um subconjunto de Path.GetInvalidFileNameChars (), e não o contrário. Path.GetInvalidPathChars () não retornará '?', Por exemplo.
Rafael Costa

1
Esta é uma boa resposta. Eu uso a lista de nomes de arquivos e a lista de caminhos de arquivo: ____________________________ string cleanData = new string (data.Where (x =>! Path.GetInvalidFileNameChars (). Contém (x) &&! Path.GetInvalidPathChars (). Contém (x))). ToArray ());
30717 goamn

27

Todas essas são ótimas soluções, mas todas elas contam Path.GetInvalidFileNameChars, que podem não ser tão confiáveis ​​quanto você imagina. Observe a seguinte observação na documentação do MSDN sobre Path.GetInvalidFileNameChars:

Não é garantido que a matriz retornada desse método contenha o conjunto completo de caracteres inválidos nos nomes de arquivos e diretórios. O conjunto completo de caracteres inválidos pode variar de acordo com o sistema de arquivos. Por exemplo, em plataformas de desktop baseadas no Windows, os caracteres de caminho inválidos podem incluir caracteres ASCII / Unicode 1 a 31, além de aspas ("), menores que (<), maiores que (>), pipe (|), backspace ( \ b), nulo (\ 0) e tabulação (\ t).

Não é melhor com o Path.GetInvalidPathCharsmétodo. Ele contém exatamente a mesma observação.


13
Então, qual é o objetivo de Path.GetInvalidFileNameChars? Eu esperaria que ele retornasse exatamente os caracteres inválidos para o sistema atual, contando com o .NET para saber em qual sistema de arquivos estou executando e apresentando os caracteres inválidos adequados. Se esse não for o caso e retornar apenas caracteres codificados, que não são confiáveis, em primeiro lugar, esse método deve ser removido, pois possui valor zero.
Jan

1
Sei que este é um comentário antigo, mas @Jan você pode escrever em outro sistema de arquivos, talvez seja por isso que exista um aviso.
fantastik78

3
@ fantastik78 bom ponto, mas neste caso eu gostaria de ter um argumento enum adicional para especificar meu FS remoto. Se houver muito esforço de manutenção (o que é mais provável), todo esse método ainda é uma má idéia, porque dá a impressão errada de segurança.
Janeiro

1
@ Jan Eu concordo totalmente com você, eu estava apenas discutindo sobre o aviso.
fantastik78

Curiosamente, esse é um tipo de caractere inválido da "lista negra". Não seria melhor "colocar na lista branca" apenas os caracteres válidos conhecidos aqui ?! Lembra-me da ideia estúpida "virusscanner" em vez de whitelisting aplicativos permitidos ....
Bernhard

26

Para nomes de arquivos:

var cleanFileName = string.Join("", fileName.Split(Path.GetInvalidFileNameChars()));

Para caminhos completos:

var cleanPath = string.Join("", path.Split(Path.GetInvalidPathChars()));

Observe que, se você pretende usá-lo como um recurso de segurança, uma abordagem mais robusta seria expandir todos os caminhos e verificar se o caminho fornecido pelo usuário é realmente filho de um diretório ao qual o usuário deve ter acesso.


18

Para iniciantes, o Trim apenas remove os caracteres do início ou do fim da string . Em segundo lugar, você deve avaliar se realmente deseja remover os caracteres ofensivos ou falhar rapidamente e informar ao usuário que seu nome de arquivo é inválido. Minha escolha é a última, mas minha resposta deve mostrar pelo menos como fazer as coisas da maneira certa e errada:

Pergunta StackOverflow mostrando como verificar se uma determinada string é um nome de arquivo válido . Observe que você pode usar o regex desta pergunta para remover caracteres com uma substituição de expressão regular (se você realmente precisar fazer isso).


Concordo especialmente com o segundo conselho.
OregonGhost 28/09/08

4
Normalmente, eu concordaria com o segundo, mas tenho um programa que gera um nome de arquivo e que pode conter caracteres ilegais em algumas situações. Como meu programa está gerando nomes de arquivos ilegais, acho apropriado remover / substituir esses caracteres. (Apenas apontando um caso de uso válido)
JDB ainda se lembra de Monica

16

A melhor maneira de remover caracteres ilegais da entrada do usuário é substituir caracteres ilegais usando a classe Regex, criar método no código por trás ou validar no lado do cliente usando o controle RegularExpression.

public string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_]+", "_", RegexOptions.Compiled);
}

OU

<asp:RegularExpressionValidator ID="regxFolderName" 
                                runat="server" 
                                ErrorMessage="Enter folder name with  a-z A-Z0-9_" 
                                ControlToValidate="txtFolderName" 
                                Display="Dynamic" 
                                ValidationExpression="^[a-zA-Z0-9_]*$" 
                                ForeColor="Red">

5
IMHO esta solução é muito melhor que outras Em vez de procurar todos os caracteres inválidos, basta definir quais são válidos.
igorushi

15

Eu uso expressões regulares para conseguir isso. Primeiro, construo dinamicamente o regex.

string regex = string.Format(
                   "[{0}]",
                   Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

Depois, chamo de removeInvalidChars.Replace para localizar e substituir. Obviamente, isso também pode ser estendido para cobrir os caracteres do caminho.


Estranho, tem funcionado para mim. Vou checar quando tiver chance. Você pode ser mais específico e explicar o que exatamente não está funcionando para você?
Jeff Yates

1
Não funcionará (pelo menos corretamente) porque você não está escapando dos caracteres do caminho corretamente, e alguns deles têm um significado especial. Consulte a minha resposta para saber como fazer isso.
Matthew Scharley

@ Jeff: Sua versão ainda é melhor que a de Matthew, se você a modificar um pouco. Consulte a minha resposta sobre como.
Jan

2
Gostaria de acrescentar também alguns outros padrões de nome de arquivo inválidos que podem ser encontrados no MSDN e estender a sua solução para o seguinte regex:new Regex(String.Format("^(CON|PRN|AUX|NUL|CLOCK\$|COM[1-9]|LPT[1-9])(?=\..|$)|(^(\.+|\s+)$)|((\.+|\s+)$)|([{0}])", Regex.Escape(new String(Path.GetInvalidFileNameChars()))), RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);
yar_shukan

13

Eu absolutamente prefiro a idéia de Jeff Yates. Funcionará perfeitamente, se você o modificar um pouco:

string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

A melhoria é apenas para escapar do regex gerado automaticamente.


11

Aqui está um trecho de código que deve ajudar o .NET 3 e superior.

using System.IO;
using System.Text.RegularExpressions;

public static class PathValidation
{
    private static string pathValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex pathValidator = new Regex(pathValidatorExpression, RegexOptions.Compiled);

    private static string fileNameValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex fileNameValidator = new Regex(fileNameValidatorExpression, RegexOptions.Compiled);

    private static string pathCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex pathCleaner = new Regex(pathCleanerExpression, RegexOptions.Compiled);

    private static string fileNameCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex fileNameCleaner = new Regex(fileNameCleanerExpression, RegexOptions.Compiled);

    public static bool ValidatePath(string path)
    {
        return pathValidator.IsMatch(path);
    }

    public static bool ValidateFileName(string fileName)
    {
        return fileNameValidator.IsMatch(fileName);
    }

    public static string CleanPath(string path)
    {
        return pathCleaner.Replace(path, "");
    }

    public static string CleanFileName(string fileName)
    {
        return fileNameCleaner.Replace(fileName, "");
    }
}

8

A maioria das soluções acima combina caracteres ilegais para o caminho e o nome do arquivo, o que está errado (mesmo quando as duas chamadas retornam o mesmo conjunto de caracteres). Eu primeiro dividiria o caminho + nome do arquivo no caminho e no nome do arquivo, depois aplicaria o conjunto apropriado se eles e depois combinaria os dois novamente.

wvd_vegt


+1: muito verdadeiro. Hoje, trabalhando no .NET 4.0, a solução regex da principal resposta reduziu todas as barras invertidas em um caminho completo. Então eu fiz uma regex para o caminho do diretório e uma regex para apenas o nome do arquivo, limpos separadamente e recombinados
dario_ramos

Isso pode ser verdade, mas isso não responde à pergunta. Eu não tenho certeza um vago 'eu faria assim' é terrivelmente útil em comparação com algumas das soluções completas já aqui (ver, por exemplo resposta da Lilly, abaixo)
Ian Grainger

6

Se você remover ou substituir por um único caractere os caracteres inválidos, poderá haver colisões:

<abc -> abc
>abc -> abc

Aqui está um método simples para evitar isso:

public static string ReplaceInvalidFileNameChars(string s)
{
    char[] invalidFileNameChars = System.IO.Path.GetInvalidFileNameChars();
    foreach (char c in invalidFileNameChars)
        s = s.Replace(c.ToString(), "[" + Array.IndexOf(invalidFileNameChars, c) + "]");
    return s;
}

O resultado:

 <abc -> [1]abc
 >abc -> [2]abc

5

Lance uma exceção.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }

4

Eu escrevi esse monstro por diversão, ele permite que você ida e volta:

public static class FileUtility
{
    private const char PrefixChar = '%';
    private static readonly int MaxLength;
    private static readonly Dictionary<char,char[]> Illegals;
    static FileUtility()
    {
        List<char> illegal = new List<char> { PrefixChar };
        illegal.AddRange(Path.GetInvalidFileNameChars());
        MaxLength = illegal.Select(x => ((int)x).ToString().Length).Max();
        Illegals = illegal.ToDictionary(x => x, x => ((int)x).ToString("D" + MaxLength).ToCharArray());
    }

    public static string FilenameEncode(string s)
    {
        var builder = new StringBuilder();
        char[] replacement;
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if(Illegals.TryGetValue(c,out replacement))
                {
                    builder.Append(PrefixChar);
                    builder.Append(replacement);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static string FilenameDecode(string s)
    {
        var builder = new StringBuilder();
        char[] buffer = new char[MaxLength];
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if (c == PrefixChar)
                {
                    reader.Read(buffer, 0, MaxLength);
                    var encoded =(char) ParseCharArray(buffer);
                    builder.Append(encoded);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static int ParseCharArray(char[] buffer)
    {
        int result = 0;
        foreach (char t in buffer)
        {
            int digit = t - '0';
            if ((digit < 0) || (digit > 9))
            {
                throw new ArgumentException("Input string was not in the correct format");
            }
            result *= 10;
            result += digit;
        }
        return result;
    }
}

1
Eu gosto disso porque evita ter duas seqüências diferentes criando o mesmo caminho resultante.
Kim

3

Eu acho que é muito mais fácil validar usando um regex e especificando quais caracteres são permitidos, em vez de tentar verificar todos os caracteres incorretos. Consulte estes links: http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html

Além disso, faça uma pesquisa por "editor de expressões regulares", eles ajudam muito. Existem alguns exemplos nos quais até o código em c # é exibido para você.


Como o .net é uma estrutura destinada a permitir a execução de programas em várias plataformas (por exemplo, Linux / Unix e Windows), considero melhor o Path.GetInvalidFileNameChars (), pois contém o conhecimento do que é ou não é '' válido no sistema de arquivos em que seu programa está sendo executado. Mesmo que seu programa nunca seja executado no Linux (talvez esteja cheio de código WPF), sempre há a chance de que um novo sistema de arquivos do Windows apareça no futuro e tenha diferentes caracteres válidos / inválidos. Rolar o seu próprio com regex é reinventar a roda e mudar um problema de plataforma para o seu próprio código.
Daniel Scott

No entanto, concordo com o seu conselho sobre editores / testadores de regex online. Acho-os inestimáveis ​​(já que as expressões regulares são coisas complicadas e cheias de sutileza que podem facilitar você, dando-lhe uma expressão regular que se comporta de uma maneira totalmente inesperada com casos extremos). Meu favorito é regex101.com (eu gosto de como ele divide o regex e mostra claramente o que ele espera corresponder). Eu também gosto bastante de debuggex.com , pois possui uma representação visual compacta de grupos de partidas, classes de personagens e outros enfeites.
Daniel Scott

3

Parece ser O (n) e não gasta muita memória em seqüências de caracteres:

    private static readonly HashSet<char> invalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string RemoveInvalidFileNameChars(string name)
    {
        if (!name.Any(c => invalidFileNameChars.Contains(c))) {
            return name;
        }

        return new string(name.Where(c => !invalidFileNameChars.Contains(c)).ToArray());
    }

1
Eu não acho que seja O (n) quando você usa a função 'Qualquer'.
II ARROWS

@IIARROWS e qual é a sua opinião?
Alexey F

Eu não sei, não parecia assim quando escrevi meu comentário ... agora que tentei calculá-lo, parece que você está certo.
II SETAS

Selecionei este por sua consideração de desempenho. Obrigado.
Berend Engelbrecht

3

Escaneando as respostas aqui, todas elas ** parecem envolver o uso de uma matriz de caracteres com caracteres inválidos para o nome do arquivo.

Concedido, isso pode ser micro-otimizador - mas, para o benefício de qualquer pessoa que esteja procurando verificar um grande número de valores como nomes de arquivos válidos, é importante notar que a criação de um hashset de caracteres inválidos trará um desempenho notavelmente melhor.

Fiquei muito surpreso (chocado) no passado com a rapidez com que um hashset (ou dicionário) supera a iteração em uma lista. Com strings, é um número ridiculamente baixo (cerca de 5-7 itens de memória). Com a maioria dos outros dados simples (referências a objetos, números etc.), o cruzamento mágico parece estar em torno de 20 itens.

Existem 40 caracteres inválidos na "lista" Path.InvalidFileNameChars. Fiz uma pesquisa hoje e há uma boa referência aqui no StackOverflow que mostra que o hashset levará um pouco mais da metade do tempo de uma matriz / lista para 40 itens: https://stackoverflow.com/a/10762995/949129

Aqui está a classe auxiliar que eu uso para higienizar caminhos. Eu esqueço agora porque eu tinha a opção de substituição sofisticada, mas está lá como um bônus fofo.

Método de bônus adicional "IsValidLocalPath" também :)

(** aqueles que não usam expressões regulares)

public static class PathExtensions
{
    private static HashSet<char> _invalidFilenameChars;
    private static HashSet<char> InvalidFilenameChars
    {
        get { return _invalidFilenameChars ?? (_invalidFilenameChars = new HashSet<char>(Path.GetInvalidFileNameChars())); }
    }


    /// <summary>Replaces characters in <c>text</c> that are not allowed in file names with the 
    /// specified replacement character.</summary>
    /// <param name="text">Text to make into a valid filename. The same string is returned if 
    /// it is valid already.</param>
    /// <param name="replacement">Replacement character, or NULL to remove bad characters.</param>
    /// <param name="fancyReplacements">TRUE to replace quotes and slashes with the non-ASCII characters ” and ⁄.</param>
    /// <returns>A string that can be used as a filename. If the output string would otherwise be empty, "_" is returned.</returns>
    public static string ToValidFilename(this string text, char? replacement = '_', bool fancyReplacements = false)
    {
        StringBuilder sb = new StringBuilder(text.Length);
        HashSet<char> invalids = InvalidFilenameChars;
        bool changed = false;

        for (int i = 0; i < text.Length; i++)
        {
            char c = text[i];
            if (invalids.Contains(c))
            {
                changed = true;
                char repl = replacement ?? '\0';
                if (fancyReplacements)
                {
                    if (c == '"') repl = '”'; // U+201D right double quotation mark
                    else if (c == '\'') repl = '’'; // U+2019 right single quotation mark
                    else if (c == '/') repl = '⁄'; // U+2044 fraction slash
                }
                if (repl != '\0')
                    sb.Append(repl);
            }
            else
                sb.Append(c);
        }

        if (sb.Length == 0)
            return "_";

        return changed ? sb.ToString() : text;
    }


    /// <summary>
    /// Returns TRUE if the specified path is a valid, local filesystem path.
    /// </summary>
    /// <param name="pathString"></param>
    /// <returns></returns>
    public static bool IsValidLocalPath(this string pathString)
    {
        // From solution at https://stackoverflow.com/a/11636052/949129
        Uri pathUri;
        Boolean isValidUri = Uri.TryCreate(pathString, UriKind.Absolute, out pathUri);
        return isValidUri && pathUri != null && pathUri.IsLoopback;
    }
}

2
public static class StringExtensions
      {
        public static string RemoveUnnecessary(this string source)
        {
            string result = string.Empty;
            string regex = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
            Regex reg = new Regex(string.Format("[{0}]", Regex.Escape(regex)));
            result = reg.Replace(source, "");
            return result;
        }
    }

Você pode usar o método claramente.


2

O nome do arquivo não pode conter caracteres de Path.GetInvalidPathChars(), +e #símbolos, e outros nomes específicos. Combinamos todas as verificações em uma classe:

public static class FileNameExtensions
{
    private static readonly Lazy<string[]> InvalidFileNameChars =
        new Lazy<string[]>(() => Path.GetInvalidPathChars()
            .Union(Path.GetInvalidFileNameChars()
            .Union(new[] { '+', '#' })).Select(c => c.ToString(CultureInfo.InvariantCulture)).ToArray());


    private static readonly HashSet<string> ProhibitedNames = new HashSet<string>
    {
        @"aux",
        @"con",
        @"clock$",
        @"nul",
        @"prn",

        @"com1",
        @"com2",
        @"com3",
        @"com4",
        @"com5",
        @"com6",
        @"com7",
        @"com8",
        @"com9",

        @"lpt1",
        @"lpt2",
        @"lpt3",
        @"lpt4",
        @"lpt5",
        @"lpt6",
        @"lpt7",
        @"lpt8",
        @"lpt9"
    };

    public static bool IsValidFileName(string fileName)
    {
        return !string.IsNullOrWhiteSpace(fileName)
            && fileName.All(o => !IsInvalidFileNameChar(o))
            && !IsProhibitedName(fileName);
    }

    public static bool IsProhibitedName(string fileName)
    {
        return ProhibitedNames.Contains(fileName.ToLower(CultureInfo.InvariantCulture));
    }

    private static string ReplaceInvalidFileNameSymbols([CanBeNull] this string value, string replacementValue)
    {
        if (value == null)
        {
            return null;
        }

        return InvalidFileNameChars.Value.Aggregate(new StringBuilder(value),
            (sb, currentChar) => sb.Replace(currentChar, replacementValue)).ToString();
    }

    public static bool IsInvalidFileNameChar(char value)
    {
        return InvalidFileNameChars.Value.Contains(value.ToString(CultureInfo.InvariantCulture));
    }

    public static string GetValidFileName([NotNull] this string value)
    {
        return GetValidFileName(value, @"_");
    }

    public static string GetValidFileName([NotNull] this string value, string replacementValue)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new ArgumentException(@"value should be non empty", nameof(value));
        }

        if (IsProhibitedName(value))
        {
            return (string.IsNullOrWhiteSpace(replacementValue) ? @"_" : replacementValue) + value; 
        }

        return ReplaceInvalidFileNameSymbols(value, replacementValue);
    }

    public static string GetFileNameError(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            return CommonResources.SelectReportNameError;
        }

        if (IsProhibitedName(fileName))
        {
            return CommonResources.FileNameIsProhibited;
        }

        var invalidChars = fileName.Where(IsInvalidFileNameChar).Distinct().ToArray();

        if(invalidChars.Length > 0)
        {
            return string.Format(CultureInfo.CurrentCulture,
                invalidChars.Length == 1 ? CommonResources.InvalidCharacter : CommonResources.InvalidCharacters,
                StringExtensions.JoinQuoted(@",", @"'", invalidChars.Select(c => c.ToString(CultureInfo.CurrentCulture))));
        }

        return string.Empty;
    }
}

O método GetValidFileNamesubstitui todos os dados incorretos para _.


2

Um alinhador para limpar a string de quaisquer caracteres ilegais para nomeação de arquivos do Windows:

public static string CleanIllegalName(string p_testName) => new Regex(string.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars())))).Replace(p_testName, "");

1
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}

0

Isso vai querer o que você quer e evitar colisões

 static string SanitiseFilename(string key)
    {
        var invalidChars = Path.GetInvalidFileNameChars();
        var sb = new StringBuilder();
        foreach (var c in key)
        {
            var invalidCharIndex = -1;
            for (var i = 0; i < invalidChars.Length; i++)
            {
                if (c == invalidChars[i])
                {
                    invalidCharIndex = i;
                }
            }
            if (invalidCharIndex > -1)
            {
                sb.Append("_").Append(invalidCharIndex);
                continue;
            }

            if (c == '_')
            {
                sb.Append("__");
                continue;
            }

            sb.Append(c);
        }
        return sb.ToString();

    }

0

Eu acho que a pergunta já não está completa respondida ... As respostas descrevem apenas o nome do arquivo limpo OU o caminho ... não os dois. Aqui está a minha solução:

private static string CleanPath(string path)
{
    string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
    Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
    List<string> split = path.Split('\\').ToList();
    string returnValue = split.Aggregate(string.Empty, (current, s) => current + (r.Replace(s, "") + @"\"));
    returnValue = returnValue.TrimEnd('\\');
    return returnValue;
}

0

Eu criei um método de extensão que combina várias sugestões:

  1. Mantendo caracteres ilegais em um conjunto de hash
  2. Filtrando caracteres abaixo do ASCII 127. Como o Path.GetInvalidFileNameChars não inclui todos os caracteres inválidos possíveis com códigos ASCII de 0 a 255. Consulte aqui e o MSDN
  3. Possibilidade de definir o caractere de substituição

Fonte:

public static class FileNameCorrector
{
    private static HashSet<char> invalid = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string ToValidFileName(this string name, char replacement = '\0')
    {
        var builder = new StringBuilder();
        foreach (var cur in name)
        {
            if (cur > 31 && cur < 128 && !invalid.Contains(cur))
            {
                builder.Append(cur);
            }
            else if (replacement != '\0')
            {
                builder.Append(replacement);
            }
        }

        return builder.ToString();
    }
}

0

Aqui está uma função que substitui todos os caracteres ilegais em um nome de arquivo por um caractere de substituição:

public static string ReplaceIllegalFileChars(string FileNameWithoutPath, char ReplacementChar)
{
  const string IllegalFileChars = "*?/\\:<>|\"";
  StringBuilder sb = new StringBuilder(FileNameWithoutPath.Length);
  char c;

  for (int i = 0; i < FileNameWithoutPath.Length; i++)
  {
    c = FileNameWithoutPath[i];
    if (IllegalFileChars.IndexOf(c) >= 0)
    {
      c = ReplacementChar;
    }
    sb.Append(c);
  }
  return (sb.ToString());
}

Por exemplo, o sublinhado pode ser usado como um caractere de substituição:

NewFileName = ReplaceIllegalFileChars(FileName, '_');

Além da resposta que você forneceu, considere fornecer uma breve explicação de por que e como isso resolve o problema.
jtate 14/05

-7

Ou você pode simplesmente fazer

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
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.