Split "full file name" into "path" + "filename"

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
badidea
Posts: 2591
Joined: May 24, 2007 22:10
Location: The Netherlands

Split "full file name" into "path" + "filename"

Post by badidea »

Hello all, I could not find any (build-in) functions for file name manipulation, so I made the following:

Code: Select all

dim as string s1 = "c:\dir\subdir\01_test.txt" 'windows
dim as string s2 = "/dir/main.bas" 'linux
dim as string s3 = "test_123.bas" 'no path
dim as string s4

function getFilePath(fullFileName as string) as string
	dim as integer sepPos = instrrev(fullFileName, any "/\")
	return mid(fullFileName, 1, sepPos)
end function

function getFileName(fullFileName as string) as string
	dim as integer sepPos = instrrev(fullFileName, any "/\")
	return mid(fullFileName, sepPos + 1)
end function

'change \ to / , note: ("\")[0] = asc("\")
function slashFileName(byval fileName as string) as string
	for i as integer = 0 to len(fileName)-1
		if fileName[i] = ("\")[0] then fileName[i] = ("/")[0]
	next
	return fileName
end function

print "s1: '" & getFilePath(s1) & "' + '" & getFileName(s1) & "'"
print "s2: '" & getFilePath(s2) & "' + '" & getFileName(s2) & "'"
print "s3: '" & getFilePath(s3) & "' + '" & getFileName(s3) & "'"
print

s4 = slashFileName(s1)

print "s1: '" & getFilePath(s1) & "' + '" & getFileName(s1) & "'"
print "s4: '" & getFilePath(s4) & "' + '" & getFileName(s4) & "'"
'slashFileName' converts backslashes to slashes, which both Windows and Linux accept in FreeBASIC I believe.
Maybe useful for someone else as well.
PaulSquires
Posts: 1002
Joined: Jul 14, 2005 23:41

Re: Split "full file name" into "path" + "filename"

Post by PaulSquires »

If you are working on the Windows platform then I urge anyone to download Jose Roca's WinFBX set of include files. They contain hundreds of useful functions that work with both ansi an unicode strings.

Here are some that split pathnames into its parts:

Code: Select all

' ========================================================================================
'  Parses a path/file name to extract component parts.
'  This function evaluates a text path/file text name, and returns a requested part of the
'  name. The functionality is strictly one of string parsing alone.
'  wszOption is one of the following words which is used to specify the requested part:
'  PATH
'        Returns the path portion of the path/file Name. That is the text up to and
'        including the last backslash (\) or colon (:).
'  NAME
'        Returns the name portion of the path/file Name. That is the text to the right
'        of the last backslash (\) or colon (:), ending just before the last period (.).
'  EXTN
'        Returns the extension portion of the path/file name. That is the last
'        period (.) in the string plus the text to the right of it.
'  NAMEX
'        Returns the name and the EXTN parts combined.
' ========================================================================================
PRIVATE FUNCTION AfxStrPathName (BYREF wszOption AS CONST WSTRING, BYREF wszFileSpec AS WSTRING) AS CWSTR
   DIM cws AS CWSTR = ""
   IF LEN(wszFileSpec) = 0 THEN RETURN cws
   SELECT CASE UCASE(wszOption)
      CASE "PATH"
         ' // Returns the path portion of file spec
         DIM nPos AS LONG = InstrRev(wszFileSpec, ANY ":/\")
         IF nPos THEN cws = MID(wszFileSpec, 1, nPos)
      CASE "NAME"
         ' // Retrieve the full filename
         cws = wszFileSpec
         DIM nPos AS LONG = InstrRev(wszFileSpec, ANY ":/\")
         IF nPos THEN cws = MID(wszFileSpec, nPos + 1)
         ' // Retrieve the filename
         nPos = InstrRev(cws, ".")
         IF nPos THEN cws = MID(cws, 1, nPos - 1)
      CASE "NAMEX"
         ' // Retrieve the name and extension combined
         DIM nPos AS LONG = InStrRev(wszFileSpec, ANY ":/\")
         IF nPos THEN cws = MID(wszFileSpec, nPos + 1) ELSE cws = wszFileSpec
      CASE "EXTN"
         ' // Retrieve the name and extension combined
         DIM nPos AS LONG = InstrRev(wszFileSpec, ANY ":/\")
         IF nPos THEN cws = MID(wszFileSpec, nPos + 1) ELSE cws = wszFileSpec
         ' // Retrieve the extension
         nPos = InStrRev(cws, ".")
         IF nPos THEN cws = MID(cws, nPos) ELSE cws = ""
   END SELECT
   RETURN cws
END FUNCTION
' ========================================================================================

PaulSquires
Posts: 1002
Joined: Jul 14, 2005 23:41

Re: Split "full file name" into "path" + "filename"

Post by PaulSquires »

Here is a few more as an example....

Code: Select all

' ========================================================================================
' Parses a path/filename and returns the file name portion. That is the text to the right
' of the last backslash (\) or colon (:), ending just before the last period (.). The file
' extension is excluded.
' ========================================================================================
PRIVATE FUNCTION AfxGetFileName (BYREF wszPath AS WSTRING) AS CWSTR
   DIM cwsPath AS CWSTR = wszPath
   DIM p AS LONG
   p = INSTRREV(cwsPath, ANY ":/\")
   IF p THEN cwsPath = MID(cwsPath, p + 1)
   p = INSTRREV(cwsPath, ".")
   IF p THEN
      cwsPath = LEFT(**cwsPath, p - 1)
   ELSE
      cwsPath = ""
   END IF
   RETURN cwsPath
END FUNCTION
' ========================================================================================

' ========================================================================================
' Parses a path/filename and returns the file name portion. That is the text to the right
' of the last backslash (\) or colon (:), ending just before the last period (.).
' ========================================================================================
PRIVATE FUNCTION AfxGetFileNameX (BYREF wszPath AS WSTRING) AS CWSTR
   DIM cwsPath AS CWSTR = wszPath
   DIM p AS LONG
   p = INSTRREV(cwsPath, ANY ":/\")
   IF p THEN RETURN MID(cwsPath, p + 1) ELSE RETURN cwsPath
END FUNCTION
' ========================================================================================

' ========================================================================================
' Parses a path/filename and returns the extension portion of the path/file name. That is
' the last period (.) in the string plus the text to the right of it.
' ========================================================================================
PRIVATE FUNCTION AfxGetFileExt (BYREF wszPath AS WSTRING) AS CWSTR
   DIM cwsPath AS CWSTR = wszPath
   DIM p AS LONG
   p = INSTRREV(cwsPath, ".")
   IF p THEN RETURN MID(cwsPath, p) ELSE RETURN cwsPath
END FUNCTION
' ========================================================================================

' ========================================================================================
' Parses a path/filename and returns the path portion. That is the text up to and including
' the last backslash (\) or colon (:).
' ========================================================================================
PRIVATE FUNCTION AfxGetPathName (BYREF wszPath AS WSTRING) AS CWSTR
   DIM cwsPath AS CWSTR = wszPath
   DIM p AS LONG
   p = INSTRREV(cwsPath, ANY ":\/")
   IF p THEN RETURN LEFT(**cwsPath, p)
   RETURN ""
END FUNCTION
' ========================================================================================
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Split "full file name" into "path" + "filename"

Post by MrSwiss »

badidea wrote:'slashFileName' converts backslashes to slashes, which both Windows and Linux accept in FreeBASIC I believe.
Maybe useful for someone else as well.
Well, I'd say not really, because of:
currently, it is to limited to slashes and, 'one way' only (to much statically coded).
(The secret behind flexible procedures is: parameters, instead of literals/globals!)

With a little bit more effort, your function can be made, far more 'general use'.
I've renamed it too, to reflect it's usability:

Code: Select all

' replaces given 'search' char, with a given 'replace' char, in Source String (src_str)
Function ReplaceChar( _
    ByRef src_str   As String, _        ' the string: to be modified
    ByVal search    As UByte, _         ' ASCII number of: char (to search for) 
    ByVal replace   As UByte _          ' ASCII number of: char (to replace with)
    ) ByRef As String                   ' modified string (as above specified)
    For i As UInteger = 0 To Len(src_str) - 1
        If src_str[i] = search Then src_str[i] = replace
    Next
    Return src_str
End Function
And the rewritten test code 'in full':

Code: Select all

function getFilePath(fullFileName as string) as string
   Dim As UShort    sepPos = instrrev(fullFileName, any "/\")
   return mid(fullFileName, 1, sepPos)
end function

function getFileName(fullFileName as string) as string
   dim As UShort    sepPos = instrrev(fullFileName, any "/\")
   return mid(fullFileName, sepPos + 1)
end function

' replaces given 'search' char, with a given 'replace' char (in SourceString)
Function ReplaceChar( _
    ByRef src_str   As String, _        ' the string: to be modified
    ByVal search    As UByte, _         ' ASCII number of: char (to search for) 
    ByVal replace   As UByte _          ' ASCII number of: char (to replace with)
    ) ByRef As String                   ' modified string (as above specified)
    For i As UInteger = 0 To Len(src_str) - 1
        If src_str[i] = search Then src_str[i] = replace
    Next
    Return src_str
End Function


dim as string s1 = "c:\dir\subdir\01_test.txt" 'windows
dim as string s2 = "/dir/main.bas" 'linux
dim as string s3 = "test_123.bas" 'no path

print "s1: '" & getFilePath(s1) & "' + '" & getFileName(s1) & "'"
print "s2: '" & getFilePath(s2) & "' + '" & getFileName(s2) & "'"
print "s3: '" & getFilePath(s3) & "' + '" & getFileName(s3) & "'"
print

Print "modified s1: "; ReplaceChar(s1, Asc("\"), Asc("/"))  ' WIN to LIN
Print "modified s2: "; ReplaceChar(s2, Asc("/"), Asc("\"))  ' LIN to WIN
Print

Print "now we replace any 'd' with 't' in 's1'"
Print "modified s1: "; ReplaceChar(s1, Asc("d"), Asc("t"))

Sleep
UEZ
Posts: 988
Joined: May 05, 2017 19:59
Location: Germany

Re: Split "full file name" into "path" + "filename"

Post by UEZ »

Here my contribution using regex.

Code: Select all

#Libpath "pcre-839-Static\win32\"
#Include "pcre.bi"

Function RegEx(Byval aPattern As String, Byval aSubject As String, aArr() As String) As Integer
  Const OVECCOUNT = 300
  
  Dim As Zstring Ptr error_
  Dim As Integer error_offset, rc, i, ovector(OVECCOUNT - 1), result
  Dim As pcre Ptr re
  
  Erase aArr
  result = 0
  
  re = pcre_compile(aPattern, 0, @error_, @error_offset, NULL)
  If re = NULL Then
    Return result
  End If
  
  i = 0
  Do
    rc = pcre_exec(re, NULL, Strptr(aSubject), Len(aSubject), i, 0, @ovector(0), OVECCOUNT)
    If rc > 0 Then
      Redim Preserve aArr(Lbound(aArr) To Ubound(aArr) + 1)
      aArr(Ubound(aArr)) = Mid(aSubject, ovector(0) + 1, ovector(1) - ovector(0))
      result += 1
      i = ovector(1)
    End If
  Loop While rc >= 0

  Return result
End Function

Sub PrintArray(aArr() As String)
  For i As Integer = Lbound(aArr) To Ubound(aArr)
    Print(Trim(aArr(i), Any Chr(10, 13, 32)))
  Next i
End Sub


Dim As String aResult(), sPathWindows, sPathLinux

sPathWindows = "c:\Windows\System32\catroot\{127D0A1D-4EF2-11D1-8608-00C04FC295EE}\Test.txt"
RegEx("[^\\*]+", sPathWindows, aResult()) 'windows
PrintArray(aResult())
Sleep
?
sPathLinux = "/dir/main.bas"
RegEx("[^\/*]+", sPathLinux, aResult()) 'linux
PrintArray(aResult())
Sleep
RegEx function and PrintArray sub taken from here: viewtopic.php?f=7&t=26602&p=245945&hili ... 4c#p245945

Output should look like this here:

Code: Select all

c:
Windows
System32
catroot
{127D0A1D-4EF2-11D1-8608-00C04FC295EE}
Test.txt

dir
main.bas
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Split "full file name" into "path" + "filename"

Post by MrSwiss »

Well, using a regex for such a simple problem, is for sure a sledgehammer approach.

Using the biggest possible tool, to drive a tiny nail into a wall! aka: going over the top!

The tool used, should be somehow related, to the problem to be solved ...
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Split "full file name" into "path" + "filename"

Post by dodicat »

My contribution using the same paths as UEZ:

Code: Select all

 
Function StringSplit(s_in As String,chars As String,result() As String) As Long
    Dim As Long ctr,ctr2,k,n,LC=len(chars)
    dim As boolean tally(Len(s_in))
    #macro check_instring()
        n=0
        while n<Lc
        If chars[n]=s_in[k] Then 
        tally(k)=true
        If (ctr2-1) Then ctr+=1
        ctr2=0
        exit while
        end if
        n+=1
       wend
    #endmacro
   
    #macro split()
    If tally(k) Then
        If (ctr2-1) Then ctr+=1:result(ctr)=Mid(s_in,k+2-ctr2,ctr2-1)
        ctr2=0
    End If
    #endmacro
    '==================  LOOP TWICE =======================
    For k  =0 To Len(s_in)-1
        ctr2+=1:check_instring()
    Next k
    If ctr Then Redim result(1 To ctr): ctr=0:ctr2=0 Else  Return 0
    For k  =0 To Len(s_in)-1
        ctr2+=1:split()
    Next k
    '===================== Last one ========================
    If ctr2>0 Then
        Redim Preserve result(1 To ctr+1)
        result(ctr+1)=Mid(s_in,k+1-ctr2,ctr2)
    End If
    Return Ubound(result)
End Function

var sPathWindows = "c:\Windows\System32\catroot\{127D0A1D-4EF2-11D1-8608-00C04FC295EE}\Test.txt"
var sPathLinux = "/dir/main.bas"

redim as string a()
 if StringSplit(sPathWindows+sPathlinux,"\/",a()) then
     for n as long=lbound(a) to ubound(a)
         print a(n)
     next
 else
     print "Error"
 end if
 sleep  
UEZ
Posts: 988
Joined: May 05, 2017 19:59
Location: Germany

Re: Split "full file name" into "path" + "filename"

Post by UEZ »

MrSwiss wrote:Well, using a regex for such a simple problem, is for sure a sledgehammer approach.
Well, separates the men from the boys. ;-)
badidea
Posts: 2591
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Split "full file name" into "path" + "filename"

Post by badidea »

Sorry, if I would like code like

Code: Select all

("[^\/*]+",
, I would program in #%$@

Edit: Really, I am not allowed link a wikipedia page because it contains a certain word? I mean this page https://tinyurl.com/d5397lx
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Split "full file name" into "path" + "filename"

Post by counting_pine »

Really. But there a few possible workarounds:
- https://en.wikipedia.org/wiki/Brainfsck (Wikipedia redirect)
- https://en.wikipedia.org/wiki/Brainf%75ck
Post Reply