Converting a string to a filename

Hello,

I am trying to find a safe way to convert any string to a filename.

Found a code used by Django (Python framework) to convert a string to a filename:

slugify function

They also get a get_valid_filename function

To migrate the python code to Xojo, I found that @Kem_Tekinay have Normalize method in his M_String module.

I wanted to know if there was already a plugin doing that?

Method should work on Linux/Mac/Windows

Regards,

Julien

I can only speak for macOS. Any Unicode is allowed. Just strip /, \ and : . If you want to save to folders that are synched to the cloud different rules may apply. For instance, I need to strip emojis for Dropbox.

Just be aware that / and \ are valid in file/folder names. You can make them straight in the Finder.

You’re both not entirely correct :slight_smile:

The \ is always valid in a file name on macOS - no need to strip that.
The “/” is synonymous to “:” - only one works depending on the type of file system function you use. For instance, with Xojo’s FolderItem.Name, you use “/”, whereas with a ShellPath, as in:

dim f as new FolderItem ("/tmp/b:c", FolderItem.PathTypeShell)
f.CreateAsFolder

You use the “:” instead, and Finder will show it as “/”. Same when you use file names in Terminal.app: You’d use a “:”, and it’ll appear in other apps as “/”.

In other words: “:” and “/” are the same, but you need to be aware where you use which one.

On the DOS/Windows side, it’s many more characters you cannot use in a file name:

:\/"?*

And maybe more. I have the info somewhere but don’t have the patience to look for it now.

I tried to translate the django slugify and it output something I am satisfied with

You need the M_Norm module contained in the M_String code that you can download here

I let the code below in case someone needs something like this

Function slugify(extends value as String, allow_unicode as boolean = False) As String
		  //Converted from slugify method from django
		  // https://github.com/django/django/blob/main/django/utils/text.py#L420
		  // Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
		  // dashes to single dashes. Remove characters that aren't alphanumerics,
		  // underscores, or hyphens. Convert to lowercase. Also strip leading and
		  // trailing whitespace, dashes, and underscores.
		  
		  value = Str(value)
		  
		  if allow_unicode then
		    value = M_Norm.NormalizeViaOS(value,M_Norm.Forms.ComposedCompatible)
		  else
		    value = M_Norm.NormalizeViaOS(value,M_Norm.Forms.DecomposedCompatible).DefineEncoding(Encodings.ASCII).DefineEncoding(Encodings.UTF8)
		  end if
		  
		  Var re As New RegEx
		  re.SearchPattern = "[^\w\s-]"
		  re.ReplacementPattern = ""
		  re.Options.ReplaceAllMatches = True
		  
		  value = re.Replace(value.Lowercase())
		  
		  Var re2 As New RegEx
		  re2.SearchPattern = "[-\s]+"
		  re2.ReplacementPattern = "-"
		  re2.Options.ReplaceAllMatches = True
		  
		  value = re2.Replace(value).Trim("-","_")
		  
		  return value
		End Function
1 Like

I wrote this years ago, if it’s of any help:

Protected Function getCompatibleFileName(myFileName As String, myTargetOS As String = "", replacementText As String = "-") As String
  'forum.xojo.com/33582-the-max-dll-issue-on-windows/25#p274969
  
  If myFileName = "" Then Return ""
  
  If myTargetOS = "" Then 'no target, so set to current OS!
    #If TargetMacOS Then
      myTargetOS = "MacOS"
      
    #ElseIf TargetWindows Then
      myTargetOS = "Windows"
      
    #ElseIf TargetLinux Then
      myTargetOS = "Linux"
      
    #ElseIf TargetiOS Then
      myTargetOS = "iOS"
    #EndIf
  End If
  
  Select Case myTargetOS
  Case "Windows", "Win"
    '\/:*?"<>|
    myFileName = myFileName.ReplaceAll("\", replacementText)
    myFileName = myFileName.ReplaceAll("/", replacementText)
    myFileName = myFileName.ReplaceAll(":", replacementText)
    
    myFileName = myFileName.ReplaceAll("*", replacementText)
    myFileName = myFileName.ReplaceAll("?", replacementText)
    myFileName = myFileName.ReplaceAll("""", replacementText)
    myFileName = myFileName.ReplaceAll("<", replacementText)
    myFileName = myFileName.ReplaceAll(">", replacementText)
    myFileName = myFileName.ReplaceAll("|", replacementText)
    
  Case "MacOS", "Mac", "Macintosh", "iOS"
    myFileName = myFileName.ReplaceAll(":", replacementText)
    
  Case "Linux"
    myFileName = myFileName.ReplaceAll("\", replacementText)
    myFileName = myFileName.ReplaceAll(":", replacementText)
    myFileName = myFileName.ReplaceAll("|", replacementText)
    
  Case "ServerFileName"
    myFileName = myFileName.ReplaceAll("\", replacementText)
    myFileName = myFileName.ReplaceAll("/", replacementText)
    myFileName = myFileName.ReplaceAll(":", replacementText)
    
    myFileName = myFileName.ReplaceAll("*", replacementText)
    myFileName = myFileName.ReplaceAll("?", replacementText)
    myFileName = myFileName.ReplaceAll("""", replacementText)
    myFileName = myFileName.ReplaceAll("<", replacementText)
    myFileName = myFileName.ReplaceAll(">", replacementText)
    myFileName = myFileName.ReplaceAll("|", replacementText)
    
    'problems within quoted filename
    myFileName = myFileName.ReplaceAll("$", replacementText)
    myFileName = myFileName.ReplaceAll("`", replacementText)
    
  Case "URL"
    'myFileName = ReplaceAll(myFileName, " ", "%20")
    myFileName = EncodingToURLMBS(myFileName)
    
  Case "URLFileName"
    myFileName = myFileName.ReplaceAll("\", replacementText)
    myFileName = myFileName.ReplaceAll("/", replacementText)
    myFileName = myFileName.ReplaceAll(":", replacementText)
    
    myFileName = myFileName.ReplaceAll("*", replacementText)
    myFileName = myFileName.ReplaceAll("?", replacementText)
    myFileName = myFileName.ReplaceAll("""", replacementText)
    myFileName = myFileName.ReplaceAll("<", replacementText)
    myFileName = myFileName.ReplaceAll(">", replacementText)
    myFileName = myFileName.ReplaceAll("|", replacementText)
    
    'problems within quoted filename
    myFileName = myFileName.ReplaceAll("$", replacementText)
    myFileName = myFileName.ReplaceAll("`", replacementText)
    'myFileName = ReplaceAll(myFileName, " ", "%20")
    myFileName = EncodingToURLMBS(myFileName)
    
  End Select
  
  '#if TargetMacOS then
  'myFileName = ReplaceAll(myFileName, ":", replacementText)
  '#elseif TargetWindows then
  ''\/:*?"<>|
  'myFileName = ReplaceAll(myFileName, "\", replacementText)
  'myFileName = ReplaceAll(myFileName, "/", replacementText)
  'myFileName = ReplaceAll(myFileName, ":", replacementText)
  '
  'myFileName = ReplaceAll(myFileName, "*", replacementText)
  'myFileName = ReplaceAll(myFileName, "?", replacementText)
  'myFileName = ReplaceAll(myFileName, """", replacementText)
  'myFileName = ReplaceAll(myFileName, "<", replacementText)
  'myFileName = ReplaceAll(myFileName, ">", replacementText)
  'myFileName = ReplaceAll(myFileName, "|", replacementText)
  '
  '#elseif TargetLinux then
  'myFileName = ReplaceAll(myFileName, "\", replacementText)
  'myFileName = ReplaceAll(myFileName, ":", replacementText)
  '#endif
  
  While myFileName.Left(1) = "." 'Don't want file to become invisible!
    myFileName = myFileName.Middle(1)
  Wend
  
  Return myFileName
    
End Function