Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 Doom9's Forum SplitFilename() function for splitting filenames [2011/11/22]
 Register FAQ Calendar Search Today's Posts Mark Forums Read

 22nd November 2011, 06:14 #1  |  Link vampiredom Registered User   Join Date: Aug 2008 Posts: 233 SplitFilename() function for splitting filenames [2011/11/22] This is loosely related to this thread and I thought of posting there but I did not want to veer too much off-topic. So, here it is... Code: ###################################################################################################################### # SplitFilename() # 2011/11/22 # a function for splitting filenames into different parts. The "which" parameter determines which parts are returned # # parameter: filename (string) # ----------------- # This should be a filename or a path. Both forward slashes and backslashes are allowed # It works with both files and folders, relative and absolute paths. # If the supplied string is a path to a folder, it should end in a / or \ # # which (integer) # --------------- # This parameter should be an integer between -2 and 4, and returns the following result: # Example: C:\Folder\Subfolder\Filename.ext # | # | -2 --> drive + folder C:\Folder\Subfolder\ # | -1 --> drive + folder + basename C:\Folder\Subfolder\Filename # | 0 --> basename + ext Filename.ext # | 1 --> drive C: # | 2 --> folder \Path\Subfolder\ # | 3 --> basename Filename # | 4 --> ext .ext # # An empty string is returned if there is no appropriate result # (such as a relative path, a filename with no path or a file with no extension) # Values outside of the range of -2 to 4 also return an empty string # When returned, drive letter always ends with : and extension always begins with a . # ####################################################################################################################### Code: function SplitFilename( \ string filename, \ int which \ ) { # first get the extension. If there is a slash after the ".", then it is not an extension but a dotted folder name ext = RightStr(filename, FindStr(RevStr(filename), ".")) ext = (FindStr(ext, "/") > 0 || FindStr(ext, "\") > 0) ? "" : ext # now determine drive name, folder name, and file basename # allow both "/" and "\" for folder names drive = LeftStr(filename, FindStr(filename, ":")) dir1 = (Findstr(filename, "\") > 0) ? LeftStr(filename, StrLen(filename) - FindStr(RevStr(filename), "\") +1) : "" dir2 = (Findstr(filename, "/") > 0) ? LeftStr(filename, StrLen(filename) - FindStr(RevStr(filename), "/") +1) : "" dir = (StrLen(dir1) > StrLen(dir2)) ? dir1 : dir2 folder = (StrLen(dir) > 0) ? RightStr(dir, StrLen(dir) - StrLen(drive)) : "" basename = MidStr(filename, StrLen(drive)+StrLen(folder)+1, StrLen(filename) - StrLen(drive) - StrLen(folder) - StrLen(ext)) # copy extension to filename and clear extension for filenames that begin with . isDotFile = (ext != "" && basename == "") ? true : false dotBase = ext basename = (isDotFile) ? dotBase : basename ext = (isDotFile) ? "" : ext (filename == "") ? "" \ : (which == -2) ? drive + folder \ : (which == -1) ? drive + folder + basename \ : (which == 0) ? basename + ext \ : (which == 1) ? drive \ : (which == 2) ? folder \ : (which == 3) ? basename \ : (which == 4) ? ext \ : "" } A quick way to test exactly how this works can be accomplished by running a script like this: Code: f = "C:\Folder\Subfolder\Filename.ext" BlankClip() subtitle("---> " + f, font="Courier New", y= 10, text_color=$FFFF00) subtitle("-2 = " + Splitfilename(f,-2), font="Courier New", y= 40, text_color=$DDDDDD) subtitle("-1 = " + Splitfilename(f,-1), font="Courier New", y= 70, text_color=$DDDDDD) subtitle(" 0 = " + Splitfilename(f, 0), font="Courier New", y=100, text_color=$DDDDDD) subtitle(" 1 = " + Splitfilename(f, 1), font="Courier New", y=130, text_color=$DDDDDD) subtitle(" 2 = " + Splitfilename(f, 2), font="Courier New", y=160, text_color=$DDDDDD) subtitle(" 3 = " + Splitfilename(f, 3), font="Courier New", y=190, text_color=$DDDDDD) subtitle(" 4 = " + Splitfilename(f, 4), font="Courier New", y=220, text_color=$DDDDDD) Last edited by vampiredom; 22nd November 2011 at 21:17. Reason: implemented suggested changes
22nd November 2011, 12:22   #2  |  Link
Gavino
Avisynth language lover

Join Date: Dec 2007
Location: Spain
Posts: 3,406
Quote:
 Originally Posted by vampiredom Code: function SplitFilename( \ string filename, \ int which \ ) { which = Default(which, 0) filename = default(filename, "") ... folder = RightStr(dir, StrLen(dir) - StrLen(drive)) basename = MidStr(filename, StrLen(dir)+1, StrLen(filename) - StrLen(dir) - StrLen(ext)) ... }
The 'default' lines are redundant, since you have made the parameters mandatory. (Remember what I said yesterday?)
To make 'which' optional (it wouldn't make sense for 'filename' to be optional), put quotes around the name in the function header.

A filename with a drive and no folder (eg "C:file.ext", meaning use current folder on drive C) will give a negative length in RightStr, producing an error. You need to do this:
Code:
        folder      = StrLen(dir)>0 ? RightStr(dir, StrLen(dir) - StrLen(drive)) : ""
basename    = MidStr(filename, StrLen(drive)+StrLen(folder)+1, StrLen(filename)-StrLen(drive)-StrLen(folder)-StrLen(ext))
Other observations:
- you don't need to use CHR(92) instead of "\", since \ has no special meaning in Avisynth;
- not sure if it is correct to regard filenames starting with a '.' as having no extension, eg a file called ".avs" will still be played by WMP as an Avisynth script.
__________________
GScript and GRunT - complex Avisynth scripting made easier

Last edited by Gavino; 22nd November 2011 at 12:57. Reason: add code for setting 'basename'

22nd November 2011, 14:09   #3  |  Link
vampiredom
Registered User

Join Date: Aug 2008
Posts: 233
Quote:
 The 'default' lines are redundant, since you have made the parameters mandatory. (Remember what I said yesterday?)
Ha, yes. Actually, they were optional... then I changed it but forgot to remove the 'defaults'. Fixed. [I had it use GetWorkingPath() from [GetSystemEnv.dll] as default filename, for example, in case this being optional sounded completely crazy.]

Quote:
 you don't need to use CHR(92) instead of "\"
I know that. It's my text editor that gets a little confused with "\", so I avoid it. I have now restored the "\" for others' reading pleasure.

Quote:
 not sure if it is correct to regard filenames starting with a '.' as having no extension, eg a file called ".avs" will still be played by WMP as an Avisynth script.
It seems to me that a file called ".avs" does have a filename but not an extension. It is kind of an odd situation.

Quote:
 A filename with a drive and no folder (eg "C:file.ext", meaning use current folder on drive C) will give a negative length in RightStr, producing an error. You need to do this:

Last edited by vampiredom; 22nd November 2011 at 14:54.

 22nd November 2011, 21:11 #4  |  Link vampiredom Registered User   Join Date: Aug 2008 Posts: 233 WinPath(), BSDPath() and StrReplace() As a "companion" to SplitFilename(), I wrote WinPath(), which translates / to \, and BSDPath(), which does the opposite. This is a way of converting filename strings to/from Windows <-> UN*X-style paths. This may be handy for interfacing with programs that provide or prefer one over the other. In addition, the StrReplace() function provided by Gavino that powers the pathname translation can be used as a general string replacement function. ** EDIT ** Thanks to Gavino and IanB for the massive improvements here Code: function WinPath(string path) { path.StrReplace("/", "\") } function BSDPath(string path) { path.StrReplace("\", "/") } function StrReplace(string s, string find, string replace) { i = s.FindStr(find) return (i == 0 ? s : s.LeftStr(i-1)+replace+s.RightStr(StrLen(s)-Strlen(find)-i+1).StrReplace(find, replace)) } so ... an example of these in action (and to test them) could be: Code: a = "C:/path/file.ext" a2 = a.WinPath() a3 = a2.StrReplace("C:\path", "C:\windows") a4 = a3.StrReplace("C:\windows\", "\relative\to\root\") a5 = a4.BSDPath() a6 = a5.StrReplace("/", " ").StrReplace("l", "!").StrReplace("o", "*").StrReplace(".", ", ") BlankClip() subtitle("---> " + a, font="Courier New", y= 10, text_color=$FFFF00) subtitle("---> " + a2, font="Courier New", y= 40, text_color=$DDDDDD) subtitle("---> " + a3, font="Courier New", y= 70, text_color=$DDDDDD) subtitle("---> " + a4, font="Courier New", y=100, text_color=$DDDDDD) subtitle("---> " + a5, font="Courier New", y=130, text_color=$DDDDDD) subtitle("---> " + a6, font="Courier New", y=160, text_color=$DDDDDD) Last edited by vampiredom; 23rd November 2011 at 00:53. Reason: used IanB's mod of Gavino's string replacement function
22nd November 2011, 21:24   #5  |  Link
Gavino
Avisynth language lover

Join Date: Dec 2007
Location: Spain
Posts: 3,406
Quote:
 Originally Posted by vampiredom the ReplaceString() function that powers the pathname translation can be used as a general string replacement function, replacing up to 400 instances per string. (I know I could use Gavino's plugin for while(){} syntax, but I wanted this to be all native AviSynth code and I figured no path would ever have more than 400 slashes)
You could do it much more concisely and efficiently, and with unlimited replacements, with a recursive function. Here's what I use:
Code:
function StrReplace(string s, string find, string replace) {
i = s.FindStr(find)
return (i == 0 ? s : s.LeftStr(i-1)+replace+s.RightStr(StrLen(s)-Strlen(find)-i+1).StrReplace(find, replace))
}
__________________
GScript and GRunT - complex Avisynth scripting made easier

Last edited by Gavino; 22nd November 2011 at 21:26.

22nd November 2011, 21:36   #6  |  Link
vampiredom
Registered User

Join Date: Aug 2008
Posts: 233
Quote:
 more concisely and efficiently, and with unlimited replacements, with a recursive function
Awesome. I do this kind of thing in other scripting languages. I don't know why I assumed it was impossible in AviSynth. Thanks so much. Big improvement and you taught me something that will be invaluable to me for future scripting.

The script at top has been edited include these improvements.

 22nd November 2011, 22:33 #7  |  Link IanB Avisynth Developer   Join Date: Jan 2003 Location: Melbourne, Australia Posts: 3,173 MidStr(string, int pos [, int length]) with defaulted length is often a more useful choice than RightStr. Code: function StrReplace(string s, string find, string replace) { i = s.FindStr(find) return (i == 0 ? s : s.LeftStr(i-1)+replace+s.MidStr(Strlen(find)+i).StrReplace(find, replace)) }
22nd November 2011, 23:04   #8  |  Link
Gavino
Avisynth language lover

Join Date: Dec 2007
Location: Spain
Posts: 3,406
Quote:
 Originally Posted by IanB MidStr(string, int pos [, int length]) with defaulted length is often a more useful choice than RightStr.
Thanks, Ian - I'd overlooked that option.
__________________
GScript and GRunT - complex Avisynth scripting made easier

23rd November 2011, 00:54   #9  |  Link
vampiredom
Registered User

Join Date: Aug 2008
Posts: 233
Quote:
 MidStr(string, int pos [, int length])
Excellent again. Now implemented above. Thanks!