Split a Filename into Path, File, and Extension (code snippet)

Description

Splits a full path filename into its components, so for example:

  • C:\users\documents\myfile.txt will return 3 variables as follows:
  1. C:\users\documents\
  2. myfile.txt
  3. txt

Requires: None

Basic Code

SplitFilename.fh_lua
function SplitFilename(strFilename)
	-- Returns the Path, Filename, and Extension as 3 values
	return string.match(strFilename, "(.-)([^\\]-([^\\%.]+))$")
end

Sample of Use

path,file,extension = SplitFilename(fhGetPluginDataFileName())
print(path,file,extension)

Advanced Version

The basic version mishandles some special cases, such as when there is no extension, or when only a folder path is supplied without an actual file:

  • D:\Family Historian Projects will return:
  1. nil but should be D:\Family Historian Projects\
  2. nil but should be empty string
  3. nil but should be empty string
  • D:\Family Historian Projects\Main\Main.fh_data will return:
  1. D:\Family Historian Projects\Main\ but should include Main.fh_data\
  2. Main.fh_data but should be empty string
  3. fh_data but should be empty string

Requires: lfs

Advanced Code

This code handles those special cases correctly.

SplitFilename.fh_lua
function SplitFilename(strFilename)
	-- Returns the Path, Filename, and Extension as 3 values
	if lfs.attributes(strFilename,"mode") == "directory" then
		local strPath = strFilename:gsub("[\\/]$","")
		return strPath.."\\","",""
	end
	strFilename = strFilename.."."
	return strFilename:match("^(.-)([^\\/]-%.([^\\/%.]-))%.?$")
end

Lua Pattern Explanation

The code uses Lua patterns similar to but not identical to regular expressions. The filename pattern matching is explained as follows:

  • ( )( ( )) the 3 pairs of parentheses capture the 3 values
  • ^ anchors the pattern to the start of the filename text
  • . matches any single character
  • - allows 0 or more repetitions to make up the path value
  • [^\\/] matches any single character except back-slash & forward-slash
  • - allows 0 or more repetitions to make up filename including full-stops but not the extension
  • %. matches the full-stop separating filename and extension
  • [^\\/%.] matches any single character except back-slash, forward-slash & full-stop
  • - allows 0 or more repetitions to make up the file extension
  • %.? caters for a possible trailing full-stop added by strFilename = strFilename.."."
  • $ anchors the pattern to the end of the filename text

In the above patterns the following apply:

  • \\ represents a single \ (that in Lua must be escaped with a \ prefix)
  • / represents a single / (to cater for Unix/Mac style folder separators)
  • %. represents a full-stop (that in patterns must be escaped with a % prefix)
  • [^set] represents the complement of the set of characters

If it is required to omit the extension from the returned filename portion then adjust the final return statement by moving the filename capture closing ) earlier before the %. separator:

	return strFilename:match("^(.-)([^\\/]-)%.([^\\/%.]-)%.?$")