Question

Note: Not sure why this is marked as duplicate as I clearly stated that I don't want to use stringByReplacingOccurrencesOfString over and over again.

I have a question regarding the special character filename.

I have implemented a program, so that when you open a file or multiple files, the program will read all these filenames and local path and store them into the NSMutableArray. This part works perfectly without a problem.

My program also need to use NSTask to manipulate these files. However, the problem is, sometimes filename will contain special characters, for example, /Users/josh/Desktop/Screen Shot 2013-03-19 at 2.05.06 PM.png.

I have to replace space with backslash and space

NSString *urlPath = [[self url] path];
urlPath = [urlPath stringByReplacingOccurrencesOfString:@"(" withString:@"\\("];
urlPath = [urlPath stringByReplacingOccurrencesOfString:@")" withString:@"\\)"];
urlPath = [urlPath stringByReplacingOccurrencesOfString:@" " withString:@"\\ "];

to: /Users/josh/Desktop/Screen\ Shot\ 2013-03-19\ at\ 2.05.06\ PM.png

so that I can manipulate the file properly.

Same for the ( and ). I also need to add backslash before that.

but there are too many special characters. ie.

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

I need to change to:

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

and not to mention other special characters (ie. accent)

Is there any easy way to put a backslash in front of each special character, as I don't want to keep calling stringByReplacingOccurrencesOfString over and over again.

Was it helpful?

Solution

As described in NSTask's documentation for the setArguments: method, there should be no need to do special quoting:

Discussion

The NSTask object converts both path and the strings in arguments to appropriate C-style strings (using fileSystemRepresentation) before passing them to the task via argv[]. The strings in arguments do not undergo shell expansion, so you do not need to do special quoting, and shell variables, such as $PWD, are not resolved.

If you feel it is necessary, can you please provide some examples of the commands you want to run in the NSTask?

[UPDATE]: I see in the comments that you indeed are using the NSTask to execute a bash shell with -c, which I had wondered about. I've generally used NSTask to execute the command directly rather than going through the shell, like this:

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/ls"];
[task setArguments:[NSArray arrayWithObjects:@"-l", self.url.path, nil]];

Can you give a more accurate example of the actual command you want to run? For example, are you piping a series of commands together? Perhaps there might be an alternate way to achieve the same results without the need for using the bash shell...

OTHER TIPS

I think you may be able to use an NSRegularExpressionSearch search.

It would look something like this

+ (NSString *) addBackslashes: (NSString *) string
{
    // First convert the name string to a pure ASCII string
    NSData *asciiData = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

    NSString *asciiString = [[[NSString alloc] initWithData:asciiData encoding:NSASCIIStringEncoding] lowercaseString];

    // Define the characters that we will replace
    NSString *searchCharacters = @"PUT IN ALL OF YOUR SPECIAL CHARACTERS HERE";
    // example NSString *searchCharacters = @"!@#$%&*()";

    // replace them
    NSString *regExPattern = [NSString stringWithFormat:@"[%@]", searchCharacters];

    string = [asciiString stringByReplacingOccurrencesOfString:regExPattern withString: [NSString stringWithFormat:@"\\%@", regExPattern] options:NSRegularExpressionSearch range:NSMakeRange(0, asciiString.length)];
    return string;
}

you could maintain a set of strings that need to be escaped and use NSScanner to build the new string by iterating the the source string and each time a problematic character is found u first add \\ to a destination string and continue coping the next chars.

NSString *sourceString = @"/Users/josh/Desktop/\"Screen Shot\" 2013-03-19 at 2\\05\\06 PM.png";
NSMutableString *destString = [@"" mutableCopy];
NSCharacterSet *escapeCharsSet = [NSCharacterSet characterSetWithCharactersInString:@" ()\\"];

NSScanner *scanner = [NSScanner scannerWithString:sourceString];
while (![scanner isAtEnd]) {
    NSString *tempString;
    [scanner scanUpToCharactersFromSet:escapeCharsSet intoString:&tempString];
    if([scanner isAtEnd]){
        [destString appendString:tempString];
    }
    else {
        [destString appendFormat:@"%@\\%@", tempString, [sourceString substringWithRange:NSMakeRange([scanner scanLocation], 1)]];
        [scanner setScanLocation:[scanner scanLocation]+1];
    }
}

NSLog(@"\n%@\n%@", sourceString, destString);

result:

/Users/josh/Desktop/Screen Shot 2013-03-19 at 2.05.06 PM.png
/Users/josh/Desktop/Screen\ Shot\ 2013-03-19\ at\ 2.05.06\ PM.png
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top