Para quem procura uma solução geral, estes podem ser os critérios comuns:
- O nome do arquivo deve ser semelhante à string.
- A codificação deve ser reversível sempre que possível.
- A probabilidade de colisões deve ser minimizada.
Para conseguir isso, podemos usar regex para corresponder a caracteres ilegais, codificá- los por cento e , em seguida, restringir o comprimento da string codificada.
private static final Pattern PATTERN = Pattern.compile("[^A-Za-z0-9_\\-]");
private static final int MAX_LENGTH = 127;
public static String escapeStringAsFilename(String in){
StringBuffer sb = new StringBuffer();
// Apply the regex.
Matcher m = PATTERN.matcher(in);
while (m.find()) {
// Convert matched character to percent-encoded.
String replacement = "%"+Integer.toHexString(m.group().charAt(0)).toUpperCase();
m.appendReplacement(sb,replacement);
}
m.appendTail(sb);
String encoded = sb.toString();
// Truncate the string.
int end = Math.min(encoded.length(),MAX_LENGTH);
return encoded.substring(0,end);
}
Padrões
O padrão acima é baseado em um subconjunto conservador de caracteres permitidos na especificação POSIX .
Se você quiser permitir o caractere de ponto, use:
private static final Pattern PATTERN = Pattern.compile("[^A-Za-z0-9_\\-\\.]");
Apenas tome cuidado com strings como "." e ".."
Se você quiser evitar colisões em sistemas de arquivos que não diferenciam maiúsculas de minúsculas, será necessário escapar de maiúsculas:
private static final Pattern PATTERN = Pattern.compile("[^a-z0-9_\\-]");
Ou escape de letras minúsculas:
private static final Pattern PATTERN = Pattern.compile("[^A-Z0-9_\\-]");
Em vez de usar uma lista de permissões, você pode optar por criar uma lista negra de caracteres reservados para seu sistema de arquivos específico. EX: Este regex é adequado para sistemas de arquivos FAT32:
private static final Pattern PATTERN = Pattern.compile("[%\\.\"\\*/:<>\\?\\\\\\|\\+,\\.;=\\[\\]]");
comprimento
No Android, 127 caracteres é o limite seguro. Muitos sistemas de arquivos permitem 255 caracteres.
Se você preferir manter a cauda, em vez da ponta da corda, use:
// Truncate the string.
int start = Math.max(0,encoded.length()-MAX_LENGTH);
return encoded.substring(start,encoded.length());
Decodificação
Para converter o nome do arquivo de volta à string original, use:
URLDecoder.decode(filename, "UTF-8");
Limitações
Como as strings mais longas são truncadas, existe a possibilidade de uma colisão de nomes durante a codificação ou corrupção durante a decodificação.