43

I'm using very tricky fighting methods :) to make a string like Fi?le*/ Name safe for using as a file name like File_Name. I'm sure there is a cocoa way to convert it. And I'm sure the best place to ask is here :)

Thank you!

Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98
cocoafan
  • 4,884
  • 4
  • 37
  • 45
  • Do search for NSScanner. The idea is to strip out invalid characters, or only included alphanumeric characters in the result. – Jordan Aug 15 '09 at 10:23
  • There is also the handy method stringByTrimmingCharactersInSet:. – cocoafan Aug 15 '09 at 10:28
  • 3
    stringByTrimmingCharactersInSet only removes characters from the start or end of the string, not from anywhere in the string (and so would be inappropriate here) – stevex Jun 18 '12 at 22:51

8 Answers8

68

This will remove all invalid characters anywhere in the filename based on Ismail's invalid character set (I have not verified how complete his set is).

- (NSString *)_sanitizeFileNameString:(NSString *)fileName {
    NSCharacterSet* illegalFileNameCharacters = [NSCharacterSet characterSetWithCharactersInString:@"/\\?%*|\"<>"];
    return [[fileName componentsSeparatedByCharactersInSet:illegalFileNameCharacters] componentsJoinedByString:@""];
}

Credit goes to Peter N Lewis for the idea to use componentsSeparatedByCharactersInSet:
NSString - Convert to pure alphabet only (i.e. remove accents+punctuation)

Community
  • 1
  • 1
johnboiles
  • 3,494
  • 1
  • 32
  • 26
17

Unless you're explicitly running the shell or implicitly running the shell by using a function such as popen or system, there's no reason to escape anything but the pathname separator.

You may also want to enforce that the filename does not begin with a full stop (which would cause Finder to hide the file) and probably should also enforce that it is not empty and is fewer than NAME_MAX characters* long.

*syslimits.h says bytes, but if you go through File Manager, it's characters. I'm not sure which is right for Cocoa.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
5

Solution in Swift 4

extension String {
    var sanitizedFileName: String {
        return components(separatedBy: .init(charactersIn: "/\:\?%*|\"<>")).joined()
    }
}

Usage:

"https://myurl.com".sanitizedFileName // = httpsmyurl.com
Charlton Provatas
  • 2,184
  • 25
  • 18
4
NSURL *fileURL = [NSURL fileURLWithPath:yourFilePath isDirectory:NO];

if (fileURL) {

    NSError *error;

    fileURL = [NSURL URLByResolvingAliasFileAtURL:fileURL options:(NSURLBookmarkResolutionWithoutUI | NSURLBookmarkResolutionWithoutMounting) error:&error];

}

Before Transfer:

/Users/XXX/Desktop/~!@#$%^&*()_+`-={}|"<>?[]\;',.: {}<>:^ .png

After Transfer:

file:///Users/johnny/Desktop/~!@%23$%25%5E&*()_+%60-=%7B%7D%7C%22%3C%3E%3F%5B%5D%5C%3B',.:%20%20%7B%7D%3C%3E%5C:%5E%20.png

tyljohnny
  • 41
  • 2
4

I iterated on johnboiles's answer, converting to Swift, and writing it as an extension:

extension String {
    var stringForFilePath: String {
        // characterSet contains all illegal characters on OS X and Windows
        let characterSet = NSCharacterSet(charactersInString: "\"\\/?<>:*|")
        // replace "-" with character of choice
        return componentsSeparatedByCharactersInSet(characterSet).joinWithSeparator("-")
    }
}

Illegal character set referenced from here.

Community
  • 1
  • 1
SeanR
  • 7,899
  • 6
  • 27
  • 38
2

And of course there's got to be a swift2 guy with an arbitrary hate list (stolen from other answers). That guy is me:

func sanitizedString(string : String) -> String {
    // put anything you dislike in that set ;-)
    let invalidFsChars = NSCharacterSet(charactersInString: "/* <>?%|")
    let components = string.componentsSeparatedByCharactersInSet(invalidFsChars)
    return components.joinWithSeparator("")
}
Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66
0

I used jhones answer but improved for iOS. because ' causes paths to become invalid. and I removed . too just to avoid wired filetypes being generated.

Now if a file name contains "sam's" becomes it "sams", fixed the problem I was encountering

+ (NSString *)_sanitizeFileNameString:(NSString *)fileName {
    NSCharacterSet* illegalFileNameCharacters = [NSCharacterSet characterSetWithCharactersInString:@"/\\?%*|\"<'.>"];
    return [[fileName componentsSeparatedByCharactersInSet:illegalFileNameCharacters] componentsJoinedByString:@""];
}
NSGodMode
  • 599
  • 1
  • 6
  • 16
-3

According to wikipedia, the most common characters that should be excluded from filenames are:

/\?%*|"<>

http://en.wikipedia.org/wiki/Filename

Given that, and since the invertedSet operation in the alternate solution can be intensive, to me the below is a cleaner approach:

NSCharacterSet *invalidFsChars = [NSCharacterSet characterSetWithCharactersInString:@"/\\?%*|\"<>"];
NSString *scrubbed = [originalStr stringByTrimmingCharactersInSet:invalidFsChars];

This way you can still allow filenames that have dash, etc.

Ismail Degani
  • 857
  • 9
  • 15
  • 4
    The problem with using stringByTrimmingCharactersInSet is that it only removes invalid characters from the start and end of the file name. – johnboiles Feb 08 '11 at 23:35
  • 2
    The colon ":" is also not a legal character for Windows systems, which could be a problem if you're accessing SMB-mounted filesystems. – adib May 12 '11 at 19:04