Excel

Determinar si un nombre de archivo es válido

Es bastante común en las aplicaciones permitir al usuario especificar una ruta y/o nombre de archivo para guardar archivos.

Como tal, una buena práctica sería validar la entrada del usuario antes de usarla a ciegas y quizás obtener errores y hacer que nuestras aplicaciones se bloqueen.

Por eso pensé en compartir rápidamente una forma posible de abordar esto.

Lo primero que hay que tener en cuenta es que Microsoft Windows tiene reglas que definen lo que no está permitido en los nombres de archivos y rutas. Estas reglas se detallan en:

Pero lo más destacado de las reglas es:

Un nombre de archivo no puede incluir

  • < > : ” / \ | ? *
  • Caracteres ASCII del 0 al 31

Además, el nombre en sí no debe ser una de las siguientes palabras reservadas:

  • CON,PRN,AUX,NULO,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,COM¹,COM²,COM³,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,LPT¹,LPT²,LPT³

Hay un par de cosas más que debemos evitar, pero estos son los aspectos más comunes a respetar.

Ahora bien, hay una variedad de formas de abordar esto, podríamos:

  • Utilice alguna API de archivo y pase el nombre y, si hay un error, sabremos que no es válido.
  • Construir y utilizar RegEx
  • Utilice VBA simple para validar la cadena

¿El nombre del archivo es válido o no? Verdadero o falso

Para el debate de hoy, elegí el segundo enfoque. Por ello, a continuación se muestra una función de ejemplo que validará si el nombre de archivo ingresado es aceptable según las reglas anteriores.

'---------------------------------------------------------------------------------------
' Procedure : Filename_IsValid
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Determine is the proposed filename is valid or not based on Windows
'               naming requirements
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: None required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFileName : Filename to validate (including extension)
'
' Usage:
' ~~~~~~
' ? Filename_IsValid("VBA Samples and Explanations.docx")
'   Returns -> True
'
' ? Filename_IsValid("VBA Samples >>> Explanations.docx")
'   Returns -> False
'
' ? Filename_IsValid("COM3.docx")
'   Returns -> False
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2024-03-25              Public Release
' 2         2024-03-28              Filename ending with '..' or '  '
'---------------------------------------------------------------------------------------
Public Function Filename_IsValid(sFilename As String) As Boolean
    On Error GoTo Error_Handler
    Dim iCounter              As Long
    Dim aChars()              As String
    Dim aNames()              As String
    Dim sFilenameWOExt        As String    'Filename without extension
    Dim IsValidFilename       As Boolean
    Const sIllegalChrs = ",:,"",/,\,|,?,*"
    Const sIllegalNames = "CON,PRN,AUX,NUL,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8" & _
          ",COM9,COM¹,COM²,COM³,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7" & _
          ",LPT8,LPT9,LPT¹,LPT²,LPT³"

    'illegal characters
    aChars = Split(sIllegalChrs, ",")
    For iCounter = 0 To UBound(aChars)
        If InStr(sFilename, aChars(iCounter)) > 0 Then
            IsValidFilename = True
            GoTo ReturnValue
        End If
    Next iCounter

    'Ascii 0 through 31
    For iCounter = 0 To 31
        If InStr(sFilename, Chr(iCounter)) > 0 Then
            IsValidFilename = True
            GoTo ReturnValue
        End If
    Next iCounter

    'Illegal names
    If InStr(sFilename, ".") > 0 Then
        sFilenameWOExt = Left(sFilename, InStrRev(sFilename, ".") - 1)
    Else
        sFilenameWOExt = sFilename    'No Extension
    End If
    aNames = Split(sIllegalNames, ",")
    For iCounter = 0 To UBound(aNames)
        If sFilenameWOExt = aNames(iCounter) Then
            IsValidFilename = True
            GoTo ReturnValue
        End If
    Next iCounter

    'End with '..' or '  '
    If Right(sFilename, 2) = ".." Or _
       Right(sFilename, 2) = "  " Then IsValidFilename = True

ReturnValue:
    Filename_IsValid = Not IsValidFilename

Error_Handler_Exit:
    On Error Resume Next
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Source: Filename_IsValid" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl  0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

Y ahí lo tienes, una función simple que puede validar si una cadena/nombre de archivo es aceptable o no.

Public Sub test()
    Dim sFile                 As String

    sFile = "SomeProposedFile*Name.xlsx"
    If Filename_IsValid(sFile) Then
        'All good, save as
    Else
        'The name is not acceptable, now what?!  MsgBox
        Debug.Print "Uh-oh"
    End If
End Sub

y si ejecutamos este sub, obtendremos el resultado en la ventana Inmediata:

Uh-oh

(porque hay un * en el nombre del archivo y ese es uno de los caracteres ilegales)

¡Más información por favor!

Lo anterior devuelve simplemente Verdadero/Falso, pero en algunos casos queremos detalles para que el usuario conozca el problema exacto para que pueda corregirlo y volver a enviar el nombre. Por lo tanto, podríamos personalizar aún más la función anterior y obtener:

'---------------------------------------------------------------------------------------
' Procedure : Filename_IsValid2
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : 
' Purpose   : Determine is the proposed filename is valid or not based on Windows
'               naming requirements
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - 
' Req'd Refs: None required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFileName : Filename to validate (including extension)
' sProblem  : Variable that returns the exact nature of the file name error
'
' Usage:
' ~~~~~~
' ? Filename_IsValid2("VBA Samples and Explanations.docx", sFileNameError)
'   Returns -> True, sFileNameError -> ''
'
' ? Filename_IsValid2("VBA Samples >>> Explanations.docx", sFileNameError)
'   Returns -> False, sFileNameError -> 'Invalid Characters '>''
'
' ? Filename_IsValid2("COM3.docx", sFileNameError)
'   Returns -> False, sFileNameError -> 'Invalid Name 'COM3''
'
' Revision History:
' Rev       Date(yyyy-mm-dd)        Description
' **************************************************************************************
' 1         2024-03-25              Public Release
' 2         2024-03-28              Filename ending with '..' or '  '
'---------------------------------------------------------------------------------------
Public Function Filename_IsValid2(sFilename As String, _
                                  ByRef sProblem As String) As Boolean
    On Error GoTo Error_Handler
    Dim iCounter              As Long
    Dim aChars()              As String
    Dim aNames()              As String
    Dim sFilenameWOExt        As String    'Filename without extension
    Dim IsValidFilename       As Boolean
    Const sIllegalChrs = ",:,"",/,\,|,?,*"
    Const sIllegalNames = "CON,PRN,AUX,NUL,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8" & _
          ",COM9,COM¹,COM²,COM³,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7" & _
          ",LPT8,LPT9,LPT¹,LPT²,LPT³"

    'illegal characters
    aChars = Split(sIllegalChrs, ",")
    For iCounter = 0 To UBound(aChars)
        If InStr(sFilename, aChars(iCounter)) > 0 Then
            IsValidFilename = True
            sProblem = "Invalid Characters '" & aChars(iCounter) & "'"
            GoTo ReturnValue
        End If
    Next iCounter

    'Ascii 0 through 31
    For iCounter = 0 To 31
        If InStr(sFilename, Chr(iCounter)) > 0 Then
            IsValidFilename = True
            sProblem = "Invalid ASCII Characters '" & iCounter & "'"
            GoTo ReturnValue
        End If
    Next iCounter

    'Illegal names
    If InStr(sFilename, ".") > 0 Then
        sFilenameWOExt = Left(sFilename, InStrRev(sFilename, ".") - 1)
    Else
        sFilenameWOExt = sFilename    'No Extension
    End If
    aNames = Split(sIllegalNames, ",")
    For iCounter = 0 To UBound(aNames)
        If sFilenameWOExt = aNames(iCounter) Then
            IsValidFilename = True
            sProblem = "Invalid Name '" & sFilenameWOExt & "'"
            GoTo ReturnValue
        End If
    Next iCounter

    'End with '..' or '  '
    If Right(sFilename, 2) = ".." Then
        IsValidFilename = True
        sProblem = "Cannot end a file name with '..'"
        GoTo ReturnValue
    End If
    If Right(sFilename, 2) = "  " Then
        IsValidFilename = True
        sProblem = "Cannot end a file name with '  '"
        GoTo ReturnValue
    End If

ReturnValue:
    Filename_IsValid2 = Not IsValidFilename

Error_Handler_Exit:
    On Error Resume Next
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Source: Filename_IsValid2" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl  0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function
Public Sub test()
    Dim sFile                 As String
    Dim sFileNameError        As String

    sFile = "COM3.docx"
    If Filename_IsValid2(sFile, sFileNameError) Then
        'All good, save as
        Debug.Print "'" & sFileNameError & "'"
    Else
        'The name is not acceptable, now what?!  MsgBox
        Debug.Print "'" & sFileNameError & "'"
    End If
End Sub

y en el caso anterior obtendríamos una salida en la ventana inmediata de:

'Invalid Name 'COM3''

¡Convenio de denominación!

Tal vez el mejor enfoque, en lugar de permitir a los usuarios tener total libertad para nombrar los archivos, sería diseñar y codificar una Convención de Nomenclatura de Archivos (FNC, por sus siglas en inglés). Esto estandariza los nombres de los archivos, los hace más fáciles de descifrar, simplifica la clasificación y puede evitar todo el problema de tener nombres de archivos potencialmente inválidos.

Normalmente en una FNC podemos ver:

  • Fecha
  • Categoría/Tema
  • Nombre
  • Revisión

20240313_Ingeniería_RFC_V12RevA.docx

El objetivo debe ser proporcionar un nombre que proporcione la mayor cantidad de información posible en el nombre más corto posible. No debe crear nombres de archivo que ocupen entre 100 y 150 caracteres.

Incluso Harvard lo recomienda:

Luego, dicho formato podría incorporarse fácilmente a un formulario con diversas opciones elegidas por los usuarios y el nombre del archivo podría generarse automáticamente en función de esas opciones.

En mi carrera, también he estado expuesto a convenciones de nombres alfanuméricos, como:

095ATP45_2.1.doc

donde el nombre en sí puede descomponerse en componentes como:

  • 095 -> RRHH
  • ATP -> Manual de usuario para nuevos empleados
  • 45 -> James Carleson
  • 2.1 -> Número de revisión

Este no es un buen enfoque, ya que requiere que uno siempre tenga en cuenta la convención de nomenclatura corporativa para descifrar lo que realmente es el documento. ¡Las cosas deberían entenderse fácilmente con solo mirar el nombre!

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba