String Programming Guide 3 (完)
Character Sets
NSCharacterSet, NSMutableCharacterSet
Character Set Basics
NSString 和 NSScanner 都有通过 NSCharacterSet 来实现其查找排序等功能
NSString *myString = @"some text in an NSString ..."; NSCharacterSet *characterSet = [NSCharacterSet uppercaseLetterCharacterSet]; NSRange letterRange = [myString rangeOfCharacterFromSet:characterSet]; NSLog(@"%lu,%lu",letterRange.location,letterRange.length); //OUTPUT: 2015-06-11 13:38:41.306 XYZPersonTest[920:59408] 16,1
Creating Character Sets
使用[NSCharacterSet ...CharacterSet]等方法创建的 NSCharacterSet 实例是不可变的, 即使赋值给一个 NSMutableCharacterSet 对象指针,但是可以使用 mutableCopy 方法来复制一份给新的指针:
NSMutableCharacterSet *workingSet = [[NSCharacterSet alphanumericCharacterSet]mutableCopy]; [workingSet addCharactersInString:@";:,."]; NSCharacterSet *finalSet = [workingSet copy];
还可以使用 Unicode 码通过 NSString 创建 NSCharacterSet
UniChar chars[] ={0x000C, 0x2028}; NSString *string = [[NSString alloc]initWithCharacters:chars length:sizeof(chars)/sizeof(UniChar)]; NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:string];
Performance considerations
尽量少使用 NSMutableCharacterSet
经常使用的可以考虑缓存起来(使用全局变量等)
如果一个 NSMutableCharacterSet 已经修改好了, 使用copy 创建一个 NSCharacterSet
尽量不要归档\压缩, 考虑使用单独的文件保存
Creating a character set file
NSData *charSetRep = [finalSet bitmapRepresentation]; NSURL *dataURL; NSError *error; BOOL result = [charSetRep writeToURL:dataURL options:NSDataWritingAtomic error:&error];
读取: characterSetWithContentsOfFile:
Standard Character Sets and Unicode Definitions
暂时用到:
letterCharacterSet
lowercaseLetterCharacterSet
uppercaseLetterCharacterSet
alphanumericCharacterSet
Scanner
Creating a Scanner
[NSScanner scannerWithString:aString]
[NSScanner localizedScannerWithString:aString]
创建之后的Scanner开始位置就在 string 的开头
NSString *aString = @"137.3 small cases of bananas"; float aFloat; NSScanner *theScanner = [NSScanner scannerWithString:aString]; while ([theScanner isAtEnd]==NO) { [theScanner scanFloat:&aFloat]; //implementation continues... }
使用 SetCaseSensitive: 方法可以设置是否区分大小写,默认是不区分大小写的
Using a Scanner
调用一次 scanner 之后,指针将会停留在结果值的后一位(如果有的话)
例如上面的例子,scanner 在第一次被调用后指针停留在137.3后面一位的空格
也可以修改指针的位置,使用 setScanLocation: ,可以跳过一段长度的字符(往前往后都可以)
NSString *banana = @"137 small cases of bananas"; NSString *separatorString =@" of"; NSScanner *aScanner = [NSScanner scannerWithString:banana]; NSInteger anInteger; [aScanner scanInteger:&anInteger]; NSString *container; [aScanner scanUpToString:separatorString intoString:&container]; NSLog(@"%ld,%@",anInteger,container); //OUTPUT: 2015-06-11 15:42:12.470 XYZPersonTest[1048:82680] 137,small cases [aScanner scanString:separatorString intoString:NULL]; NSString *product; product = [[aScanner string]substringFromIndex:[aScanner scanLocation]]; NSLog(@"%@",product); //OUTPUT: 2015-06-11 15:46:48.905 XYZPersonTest[1069:83584] of bananas
Example
NSString *aString = @"Product: Acme Potato Peeler; Cost: 0.98 73"; NSString *bString = @"Product: Chef Pierre Pasta Fork; Cost: 0.75 19"; NSString *cString = @"Product: Chef Pierre Colander; Cost: 1.27 2"; NSString *exampleString = [NSString stringWithFormat:@"%@\n%@\n%@",aString,bString,cString]; NSCharacterSet *semicolonSet; NSScanner *theScanner; NSString *PRODUCT = @"Product:"; NSString *COST = @"Cost:"; NSString *productName; float productCost; NSInteger productSold; semicolonSet = [NSCharacterSet characterSetWithCharactersInString:@";"]; theScanner = [NSScanner scannerWithString:exampleString]; while ([theScanner isAtEnd]==NO) { if ([theScanner scanString:PRODUCT intoString:NULL] && [theScanner scanUpToCharactersFromSet:semicolonSet intoString:&productName] && [theScanner scanString:@";" intoString:NULL] && [theScanner scanString:COST intoString:NULL] && [theScanner scanFloat:&productCost] && [theScanner scanInteger:&productSold] ){ NSLog(@"Sales of %@: $%1.2f",productName,productCost*productSold); } } /** OUTPUT: 2015-06-11 16:01:10.672 XYZPersonTest[1081:86284] Sales of Acme Potato Peeler: $71.54 2015-06-11 16:01:10.673 XYZPersonTest[1081:86284] Sales of Chef Pierre Pasta Fork: $14.25 2015-06-11 16:01:10.673 XYZPersonTest[1081:86284] Sales of Chef Pierre Colander: $2.54 */
Localization
scanner 内部的一些行为与区域有关, 如果程序涉及到这些处理, 需要改用[NSScanner localizedScannerWithString:aString]来创建 Scanner
或者使用 setLocale: 来设置区域
String Representations of File Paths
NSString 提供了很多用来处理文件系统路径的方法, 可以展开一个路径字符串(目录,文件名,文件拓展名,"~me"这种不知道叫啥,创建用户目录,清除符号链接,清除多余的斜杠,清除上级和当前目录".","..")
如果可以的话尽量使用 NSURL 来代替 NSString 来表示路径, 效率更加高
Reoresenting a Path
一般来说,'/'是目录分隔符,'.'是文件拓展名分隔符,绝对路径开头是'/',用户路径开头'~用户名/'
使用 stringByStandardizingPath 创建一个路径字符串,他还可以顺便帮你处理:
展开用户路径符号'~用户名';
清除没啥用的空白和多余的斜杠(如'//','/./');
自动处理路径字符串中的上级目录'../'符号;
NSString *path = @"/user/bin/./grep"; NSString *standardizedPath = [path stringByStandardizingPath]; NSLog(@"%@",standardizedPath); //OUTPUT: 2015-06-12 10:25:51.590 XYZPersonTest[651:19821] /user/bin/grep path = @"~adolsai";//当前用户存在才能自动转成绝对路劲? standardizedPath = [path stringByStandardizingPath]; NSLog(@"%@",standardizedPath); //OUTPUT: 2015-06-12 10:28:55.388 XYZPersonTest[668:20344] /Users/adolsai path = @"~me";//这个就不行了 standardizedPath = [path stringByStandardizingPath]; NSLog(@"%@",standardizedPath); //OUTPUT: 2015-06-12 10:30:52.339 XYZPersonTest[677:20978] ~me path = @"/user/include/objc/.."; standardizedPath = [path stringByStandardizingPath]; NSLog(@"%@",standardizedPath); //OUTPUT: 2015-06-12 10:32:16.096 XYZPersonTest[685:21428] /user/include path = @"/private/usr/include";//这个不行,为啥捏 standardizedPath = [path stringByStandardizingPath]; NSLog(@"%@",standardizedPath); //OUTPUT: 2015-06-12 10:33:08.933 XYZPersonTest[693:21630] /private/usr/include
User Directories
使用stringByExpandTildePath展开用户目录
NSString *meHome = [@"~adolsai" stringByExpandingTildeInPath]; NSLog(@"%@",meHome); //OUTPUT: 2015-06-12 10:55:30.329 XYZPersonTest[716:24666] /Users/adolsai NSString *mePublic = [@"~adolsai/Public" stringByExpandingTildeInPath]; NSLog(@"%@",mePublic); //OUTPUT: 2015-06-12 11:06:53.504 XYZPersonTest[745:27716] /Users/adolsai/Public
访问用户目录:
//尽量不要这样进入标准用户文件夹,这里使用用户目录下的"Documents"文件夹 NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingString:@"/Documents"]; NSLog(@"%@",documentsDirectory); //OUTPUT: 2015-06-12 12:30:37.967 XYZPersonTest[819:43322] /Users/adolsai/Documents //应该直接用 NSSearchPathForDirectoriesInDomains来定位当前用户的标准文件夹 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES); if ([paths count]>0){ documentsDirectory = [paths objectAtIndex:0]; } NSLog(@"%@",documentsDirectory); //OUTPUT: 2015-06-12 12:30:37.967 XYZPersonTest[819:43322] /Users/adolsai/Library/Documentation
Paths Components
三个比较常用的 NSString 中截取路径的方法:
NSString *documentPath = @"~adolsai/Public/Demo/readme.txt"; NSString *documentDirectory = [documentPath stringByDeletingLastPathComponent]; NSLog(@"%@",documentDirectory); //OUTPUT: 2015-06-12 12:41:05.655 XYZPersonTest[855:46465] ~adolsai/Public/Demo NSString *documentFilename = [documentPath lastPathComponent]; NSLog(@"%@",documentFilename); //OUTPUT: 2015-06-12 12:41:05.656 XYZPersonTest[855:46465] readme.txt NSString *documentExtension = [documentPath pathExtension]; NSLog(@"%@",documentExtension); //OUTPUT: 2015-06-12 12:41:05.656 XYZPersonTest[855:46465] txt
File Name Completion
在某个目录下匹配文件用的
//"/Users/adolsai/Public/Demo/" 目录下有"ReadMe.txtreadme.rtfreadme.txt1recondite.txttext.txt 文件 //这里不知道为什么不能用用户路径,我用~adolsai/Public/Demo/r的话会找不到文件.... NSString *partialPath = @"/Users/adolsai/Public/Demo/r"; NSString *longestCompletion; NSArray *outputArray; unsigned long allMatches = [partialPath completePathIntoString:&longestCompletion caseSensitive:NO matchesIntoArray:&outputArray filterTypes:nil]; NSLog(@"%ld,%@,%@",allMatches,longestCompletion,outputArray); /** OUTPUT: 2015-06-12 12:53:53.429 XYZPersonTest[954:50337] 4,/Users/adolsai/Public/Demo/re,( "/Users/adolsai/Public/Demo/readme.rtf", "/Users/adolsai/Public/Demo/ReadMe.txt", "/Users/adolsai/Public/Demo/readme.txt1", "/Users/adolsai/Public/Demo/recondite.txt" ) */
选择指定的文件类型:
NSArray *filterTypes = @[@"txt",@"rtf"]; allMatches = [partialPath completePathIntoString:&longestCompletion caseSensitive:NO matchesIntoArray:&outputArray filterTypes:filterTypes]; NSLog(@"%ld,%@,%@",allMatches,longestCompletion,outputArray); /* OUTPUT 2015-06-12 14:21:49.484 XYZPersonTest[1140:66258] 3,/Users/adolsai/Public/Demo/re,( "/Users/adolsai/Public/Demo/readme.rtf", "/Users/adolsai/Public/Demo/ReadMe.txt", "/Users/adolsai/Public/Demo/recondite.txt" ) */
Drawing Strings
可以直接使用 NSView 的drawAtPoint:withAttributes:函数来绘制 NSString 到 NSView 里面
这里的 attributes 可以参考 NSAttributesString
也可以使用 NSLayoutManager 来绘制,参考Cocoa Text Architeure Guide