Function to check filename validity

263 views
Skip to first unread message

Pete

unread,
May 29, 2019, 2:08:43 AM5/29/19
to Harbour Users
Hi all,

What is the best way to check if a certain string can be used as a valid filename?
Basically, I'm looking for a function that receives a string  as argument (passed by reference)
and proceeds as follow:
- checks if the passed string can safely be used as filename
  on the current O/S (or better, on any O/S).
- If yes, then return .T.
 
- If not, then..
  - if passed value is string:
    - if it contains invalid characters attempts to remove them
    - if Len(cString)> MAX_ALLOWED_PATHNAME_LENGTH then
      truncate it to MAX_ALLOWED_PATHNAME_LENGTH,
    - checks again (recursion call) whether the remaining part
      can safely be used as filename and return .T. or .F.

  - if passed value is not a string (i.e.: number, date et.c.):
    - attempts to convert it to string
    - checks again (recursion call) whether the remaining part
      can safely be used as filename and return .T. or .F.

  - Otherwise, return .F.

I don't know if such a function is already available. Any suggestion is welcome.
(Ok, yes, I could try to implement it, myself, but I prefer to be compatible
with the old good rule of not "re-inventing the wheel" etc. :->)

regards,
Pete

Jayadev U

unread,
May 29, 2019, 2:49:39 AM5/29/19
to harbou...@googlegroups.com

Hi,

 

I pass the string to the following ValidExpression function, it substitutes all invalids with an underscore.  You may modify it to suit your purposes.

 

/* code begins */

function validExpression(cInstring)

  local nIter,cThisLetter,nPos := 0

  local cInvalids := "~!@'#$%^&*()-+\.:;-<>"+space(1)

  local nLenString := LEN(cInString)

  local cOutValue := ''

  for nIter = 1 TO nLenString

    cThisLetter := SUBST(cInString,nIter,1)

    IF !(cThisLetter $ cInvalids)

      cOutValue += cThisLetter

    else

      cOutValue += "_"

    ENDIF

  NEXT

  if substr(cOutvalue,-1) = "_"

   cOutvalue := substr(cOutvalue,1,nLenstring-1)

  endif

RETURN cOutValue

/*Code Ends*/

 

HTH,

 

Warm regards,

 

Jayadev

--
--
You received this message because you are subscribed to the Google
Groups "Harbour Users" group.
Unsubscribe: harbour-user...@googlegroups.com
Web: http://groups.google.com/group/harbour-users

---
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to harbour-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/harbour-users/f9f087c4-9969-473c-9ac3-ac960e5cab28%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Serge Girard

unread,
May 29, 2019, 3:09:34 AM5/29/19
to Harbour Users
Hi Jayadev,

I shortened your solution a bit:

FUNCTION valid2Expression(cInstring)
/********************************/
LOCAL nIter
LOCAL cInvalids  := " ~!@'#$%^&*()-+\.:;-<>" 
 
FOR nIter := 1 TO LEN(cInvalids)
   cInstring := STRTRAN(cInstring, SUBSTR(cInvalids, nITER,1) , '_')
NEXT
 
RETURN cInstring

Serge

Op woensdag 29 mei 2019 08:08:43 UTC+2 schreef Pete:

Jayadev Urath

unread,
May 29, 2019, 3:20:21 AM5/29/19
to Harbour Users
Hi Serge,

Nice implementation. Thanks.

However you missed out the space character, it can also be an invalid character in filename.

Warm regards,

Jayadev


--
--
You received this message because you are subscribed to the Google
Groups "Harbour Users" group.
Unsubscribe: harbour-user...@googlegroups.com
Web: http://groups.google.com/group/harbour-users

---
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to harbour-user...@googlegroups.com.

Alain Aupeix

unread,
May 29, 2019, 3:30:24 AM5/29/19
to harbou...@googlegroups.com
Le 29/05/2019 à 09:20, Jayadev Urath a écrit :
> Hi Serge,
>
> Nice implementation. Thanks.
>
> However you missed out the space character, it can also be an invalid
> character in filename.

Surely not, just use "" around the filename to use it with space after get

A+
--
------------------------------------------------------------------------
Alain Aupeix
http://jujuland.pagesperso-orange.fr/
http://pissobi-lacassagne.pagesperso-orange.fr/
------------------------------------------------------------------------
U.buntu 12.04 & Xu.buntu 16.04 | G.ramps 3.4.9-1 | H.arbour 3.2.0dev
(2019-04-11 15:33) | Hw.Gui (2786)
------------------------------------------------------------------------

Serge Girard

unread,
May 29, 2019, 3:33:59 AM5/29/19
to Harbour Users
First char of cInvalids  is a space!
Serge

Op woensdag 29 mei 2019 09:20:21 UTC+2 schreef Jayadev:

Web: http://groups.google.com/group/harbour-users

---
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to harbou...@googlegroups.com.
Message has been deleted

Pete

unread,
May 29, 2019, 6:16:32 AM5/29/19
to Harbour Users
Hi guys,
Thanks for the replies! I took some ideas from your posts.

Just a comment regarding invalid chars:
 - Alain is right that space is valid.
 - Not all chars posted by Jayadev are invalids, although some of them (like f.e.:  ^&%; ) are quite unusual!

However, since I was in a hurry, finally didn't avoid to "invent the wheel".;-)
Below is my implementation.
It'd be useful to get your feedback, particularly bug report, if any.
(In fact, I'm almost sure you'd find some... :-) )

#define MAX_FILENAME_LENGTH 257
FUNCTION
IsValidFName( /*@*/ xName, cExt, lSpaceScore, lCheckMaxRelative, cTargetPath, lCheckExists )
    LOCAL cInvalidChars
, aTmp
    LOCAL nI
, cType

    IF
Empty( xName )
        RETURN
.F.
    ENDIF

    cType
:= ValType( xName )

   
// acceptable values can be of string, numeric, date or timestamp type
    DO CASE
        CASE cType
== "N"
            xName
:= hb_ntos( xName )
        CASE cType $
"D"
            xName
:= hb_DtoC( xName )
        CASE cType $
"T"
            xName
:= hb_TSToStr( xName, .T. )
        OTHERWISE
            IF
! HB_ISSTRING( xName )
                RETURN
.F.
            ENDIF
    ENDCASE

   
// invalid chars for windows O/S. Linux guys invited to adapt it as needed.
    cInvalidChars
:= '<>:"/\|?*.'
    FOR nI
:= 0 TO 31
        cInvalidChars
+= Chr( nI )
    NEXT
   
// prepare to remove invlaid chars
    aTmp
:= {}
    FOR nI
:= 1 TO Len( cInvalidChars )
       
AAdd( aTmp, "" )
    NEXT
    xName
:= hb_StrReplace( xName, cInvalidChars, aTmp )
   
   
// nothing remained?
    IF
Len( xName ) < 1
        RETURN
.F.
    ENDIF

   
// truncate to valid max length
    xName
:= Left( xName, MAX_FILENAME_LENGTH )
       
   
// transform spaces to underscores, if asked
    IF hb_DefaultValue
( lSpaceScore, .F. )
        xName
:= StrTran( xName, " ", "_" )
    ENDIF

   
// now adjust length to be valid when conjuncted to target path
    hb_Default
( @cTargetPath, hb_cwd() )
    cTargetPath
:= hb_DirSepAdd( cTargetPath )

    IF hb_DefaultValue
( lCheckMaxRelative, .T. )
        nI
:= Len( cTargetPath ) + Len( xName )
       
? nI
        IF nI
> MAX_FILENAME_LENGTH
            nI
:= MAX_FILENAME_LENGTH - (nI - MAX_FILENAME_LENGTH)
            xName
:= Left( xName, nI )
        ENDIF
    ENDIF

   
// finally add optional extension and check whether the filename
   
// already exists into target path (default current path)
    lCheckExists
:= ( hb_DefaultValue( lCheckExists, .F. ) .OR. ! Empty( cExt ) )
    IF lCheckExists
        xName
:= hb_FNameExtSet( xName, hb_DefaultValue( cExt, "" ) )
        IF hb_vfExists
( cTargetPath+xName )
            RETURN
.F.
        ENDIF
    ENDIF

    RETURN
.T.

Any suggestion/correction welcome!

regards,
Pete

carloskds

unread,
May 29, 2019, 12:00:09 PM5/29/19
to Harbour Users
harbour can using regex,


FUNCTION ValidaFileName( cRuc, lShowError )
   LOCAL cRegEx := "^[a-zA-Z0-9](?:[a-zA-Z0-9 ._-]*[a-zA-Z0-9])?\.[a-zA-Z0-9_-]+$"
   LOCAL lIsValid := FALSE

   DEFAULT lShowError TO FALSE
#ifdef __XHARBOUR__
   IF ( AllTrim( cRuc ) LIKE cRegEx )
#else
   IF hb_RegexLike( cRegEx, AllTrim( cRuc ) )
#endif
      lIsValid := TRUE
   ENDIF

   IF lShowError .and. !lIsValid
      Alert( "File name not valid!" )
   ENDIF

RETURN lIsValid
Message has been deleted

Pete

unread,
May 30, 2019, 6:59:19 AM5/30/19
to Harbour Users
 Hi,

Indeed, "regex-es" are extremely effective and helpful.
(sometimes, though, can contribute to mental destabilization
of programmer, due to their "fatal" complexity! :-))

thanks for the code!

regards,
Pete

Reply all
Reply to author
Forward
0 new messages