2009-07-20 13 views
50

Estoy haciendo una aplicación multiplataforma que cambia el nombre de los archivos en función de los datos recuperados en línea. Me gustaría desinfectar las cadenas que tomé de una API web para la plataforma actual.¿Existe un método Java multiplataforma para eliminar el nombre de archivo de caracteres especiales?

Sé que las diferentes plataformas tienen diferentes requisitos de nombre de archivo, por lo que me preguntaba si hay una forma de plataforma cruzada para hacer esto.

Editar: En las plataformas de Windows no se puede tener un signo de interrogación '?' en un nombre de archivo, mientras que en Linux, puede. Los nombres de los archivos pueden contener dichos caracteres y me gustaría que las plataformas que los admiten los mantengan, pero en caso contrario, elimínelos.

Además, preferiría una solución Java estándar que no requiera bibliotecas de terceros.

+0

Ben, podría dar algunos ejemplos? – OscarRyz

+0

Agregué el comentario del signo de interrogación a mi pregunta. –

Respuesta

4

No está claro por su pregunta, pero ya que está planeando aceptar nombres de rutas de un formulario web (?) Probablemente deba bloquear intentos para cambiar el nombre de ciertas cosas; p.ej. "C: \ Archivos de programa". Esto implica que necesita canonicalizar los nombres de ruta para eliminar "." y "..." antes de hacer sus comprobaciones de acceso.

Dado que, no intentaría eliminar los caracteres ilegales. En su lugar, usaría "new File (str) .getCanonicalFile()" para generar las rutas canónicas, luego verificará que satisfagan las restricciones de sandboxing, y finalmente use "File.exists()", "File.isFile()" , etc. para verificar que el origen y el destino sean kosher y que no sean el mismo objeto del sistema de archivos. Me encargaría de los personajes ilegales al intentar hacer las operaciones y atrapar las excepciones.

24

Como se ha sugerido en otros lugares, esto no es lo que generalmente desea hacer. Por lo general, es mejor crear un archivo temporal utilizando un método seguro como File.createTempFile().

No debe hacer esto con una lista blanca y solo mantener "buenos" caracteres. Si el archivo está formado únicamente por caracteres chinos, le quitará todo. No podemos usar una lista blanca por esta razón, tenemos que usar una lista negra.

Linux prácticamente permite todo lo que puede ser un verdadero dolor. Limitaría a Linux a la misma lista que limitas a Windows para que te ahorres dolores de cabeza en el futuro.

Al usar este fragmento de C# en Windows, produje una lista de caracteres que no son válidos en Windows. Hay bastantes más personajes en esta lista de los que puede pensar (41), por lo que no recomendaría tratar de crear su propia lista.

 foreach (char c in new string(Path.GetInvalidFileNameChars())) 
     { 
      Console.Write((int)c); 
      Console.Write(","); 
     } 

Aquí hay una clase de Java simple que 'limpia' un nombre de archivo.

public class FileNameCleaner { 
final static int[] illegalChars = {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}; 
static { 
    Arrays.sort(illegalChars); 
} 
public static String cleanFileName(String badFileName) { 
    StringBuilder cleanName = new StringBuilder(); 
    for (int i = 0; i < badFileName.length(); i++) { 
     int c = (int)badFileName.charAt(i); 
     if (Arrays.binarySearch(illegalChars, c) < 0) { 
      cleanName.append((char)c); 
     } 
    } 
    return cleanName.toString(); 
} 
} 

EDIT: Como Stephen sugirió que probablemente también debe verificar que estos archivos sólo accede a ocurrir dentro del directorio que lo permitan.

La siguiente respuesta tiene un código de muestra para establecer un contexto de seguridad personalizado en Java y luego ejecutar el código en esa 'caja de arena'.

How do you create a secure JEXL (scripting) sandbox?

+0

Buen ejemplo de Java, pero ¿por qué no incluiste la barra diagonal (47)? – THelper

+0

No tengo idea de por qué no está en la lista. En realidad, nos encontramos con este problema en el código de producción. He corregido la respuesta para incluir 47. Gracias. –

+0

Awesome answer. gracias – Ponmalar

6

Hay una buena muy integrada en la solución de Java - Character.isXxx().

Trate Character.isJavaIdentifierPart(c):

String name = "name.é[email protected]#$%^&*(){}][/=?+-_\\|;:`~!'\",<>"; 
StringBuilder filename = new StringBuilder(); 

for (char c : name.toCharArray()) { 
    if (c=='.' || Character.isJavaIdentifierPart(c)) { 
    filename.append(c); 
    } 
} 

El resultado es "name.é $ _".

+0

bien, así que es una forma conservadora y no cumple con la pregunta original por completo (multiplataforma), pero funcionó para mí :) –

+3

Elimina el guión que es válido para nombres de archivo (al menos en Windows) pero hace el trabajo , de todos modos, creo que Apache Commons FilenameUtils debería incorporar una forma de plataforma cruzada para hacer esto –

+0

y también elimina "@", lo que también es válido en Windows. – azerafati

17

o simplemente hacer esto:

String filename = "A20/B22b#öA\\BC#Ä$%ld_ma.la.xps"; 
String sane = filename.replaceAll("[^a-zA-Z0-9\\._]+", "_"); 

Resultado: A20_B22b_A_BC_ld_ma.la.xps

Explicación:

[a-zA-Z0-9\\._] coincide con una carta de az inferior o mayúsculas, números, puntos y guiones

[^a-zA-Z0-9\\._] es el inverso. es decir, todos los caracteres que no coinciden con la primera expresión

[^a-zA-Z0-9\\._]+ es una secuencia de caracteres que no coinciden con la primera expresión

Así que cada secuencia de caracteres que no consiste de caracteres de a-z, 0-9 o. _ será reemplazado.

+4

Esto funciona en un nombre de archivo que usa solo letras en inglés. Si el archivo está formado únicamente por caracteres chinos, le quitará todo. No podemos usar listas blancas en cadenas para quitar los caracteres incorrectos por esta razón, tenemos que usar listas negras. –

+0

Eche un vistazo aquí: http://stackoverflow.com/questions/9576384/use-regular-expression-to-match-any-chinese-character-in-utf-8-concoding debería funcionar si utiliza Java 7 – Dirk

+0

@Dirk Downvoted porque la expresión regular no es la solución aquí. ¿Qué sucede si los nombres de los archivos están en varios idiomas? –

5

Este es el código que utilizo:

public static String sanitizeName(String name) { 
    if(null == name) { 
     return ""; 
    } 

    if(SystemUtils.IS_OS_LINUX) { 
     return name.replaceAll("/+", "").trim(); 
    } 

    return name.replaceAll("[\u0001-\u001f<>:\"/\\\\|?*\u007f]+", "").trim(); 
} 

SystemUtils es de Apache commons-lang3

+0

sin SystemUtils: if (File.separatorChar == '/') { return name.replaceAll ("/ +", "") .trim(); } –

9

Esto se basa en la respuesta aceptada por Sarel Botha que funciona muy bien, siempre y cuando no se encuentra ningún carácter exterior de el Basic Multilingual Plane. Si necesita ayuda completa de Unicode (y quién no lo hace) utiliza este código en lugar de Unicode que es segura:

public class FileNameCleaner { 
    final static int[] illegalChars = {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}; 

    static { 
    Arrays.sort(illegalChars); 
    } 

    public static String cleanFileName(String badFileName) { 
    StringBuilder cleanName = new StringBuilder(); 
    int len = badFileName.codePointCount(0, badFileName.length()); 
    for (int i=0; i<len; i++) { 
     int c = badFileName.codePointAt(i); 
     if (Arrays.binarySearch(illegalChars, c) < 0) { 
     cleanName.appendCodePoint(c); 
     } 
    } 
    return cleanName.toString(); 
    } 
} 

Los cambios clave aquí:

  • Uso codePointCount i.c.w. length en lugar de sólo length
  • uso codePointAt en lugar de charAt
  • uso appendCodePoint en lugar de append
  • No hay necesidad de echar a char s int s. De hecho, nunca debe tratar con char s, ya que básicamente se han roto para cualquier cosa fuera de BMP.
+0

Puede usar funciones estándar y trabajar con caracteres: solo tiene que omitir el carácter que sigue al carácter de par suplente. Además, los caracteres no necesitan ser convertidos a tipos numéricos, son numéricos por diseño. – nekavally

0

Si desea utilizar más que como [A-Za-Z0-9], a continuación, comprobar MS Naming Conventions, y no se olvide de filtrar" ... personajes cuyas representaciones son número entero en el rango de 1 a 31 , ... ", como el ejemplo de Aaron Digulla. El código, p. de David Carboni no sería suficiente para estos caracteres.

Cuestiones relacionadas