String Programming Guide for Cocoa--字符串编程指南

描述了如何创建、搜索、连接和绘制字符串。还描述了 字符集,它可以让你搜索一个字符串中的一组字符,还有scanner(扫描器),让你转换数字到字符串并且vice versa(反之亦然).

一、Strings:字符串
NSString和NSMutableString
可以通过length属性获得其包含的Unicode字符的数量。
通过characterAtIndex:获得特定位置的Unicode字符。 这两个原始的方法提供了字符串的基本的访问。
更多的时候,字符串被当作单一的实体使用:例如比较两个字符串,当作子字符串搜索他们,组合他们到一个新的字符串,等等。
如果你需要一个字符一个字符地访问字符串对象,你必须理解Unicode字符编码---特别是相关字符组成的顺序。

二、创建和转换字符串对象:
NSString的大多数创建方法都基于不同的字符编码。虽然字符串对象总是通过Unicode展示内容,但是他们可以和其他许多字符编码互相转换,例如 7-bit ASCII,ISO Latin 1,EUC,和Shift-JIS.
类方法availableStringEncodings返回支持的编码集。你可以在转换一个C String到或从一个字符串对象时,显式指定一个编码,或使用默认的C String Encoding,类方法 defaultCStringEncoding返回这个值,但因为平台不同,实际的返回值并不一样。

1.创建字符串:
最简单的方法是使用Obj-C @“。。。。”
注意:当你使用这种方式创建一个字符串常量时,你应该使用UTF-8字符。这样的对象在编译时被创建并存在于你的程序的整个执行期间。编译器保持这些对象常量唯一,并且他们永远不会被销毁。你也可以直接给这些常量发送消息,就像对其他字符串发送消息一样。

2,NSString from C Strings and Data:
要从C String创建一个NSString对象,你使用initWithCString:encoding:方法。你必须正确地指定C String的字符编码。其他类似的方法允许你通过许多encodings来创建字符串,如 initWithUTF8String: 等等
方法initWithData:encoding:允许你从NSData对象创建字符串。

3,字符串变量:
使用stringWithFormat: 或 initWithFormat: (或 localizedStringWithFormat:) 创建
使用stringByAppendingString: 和 stringByAppendingFormat:

4,展示给用户的字符串:
你需要考虑本地化的重要性。一般,你应该避免直接在代码中创建用户可见的字符串。取而代之的,你应该使用字符串作为一个localization dictionary中的key,然后通过用户选择的语言,转换一下字符串。一般这包含使用NSLocalizedString和类似的宏,如下所示:
NSString *greeting = NSLocalizedStringFromTable(@"Hello", @"greeting to present in first launch panel",@"greetings");

5,组合和提取字符串:
stringByAppendingString:
你可以合并字符串通过initWithFormat:  、 stringWithFormat: 和 stringByAppendingFormat:
你可以提取字符串substringToIndex: , substringFromIndex:  和 substringWithRange: 方法。
你可以分隔字符串到子字符串数组,使用 componentsSeparatedByString:
你也可以连接数组为一个字符串,使用 componentsJoinedByString:

6,获得C Strings:
要从一个字符串对象获得一个C String,推荐使用UTF8String。这返回一个const char * using UTF8 string encoding。
const char *cString=[@"Hello,world" UTF8String];
返回的C String是一个临时对象,如果你想保留它,你需要创建一个C buffer并copy内容到其中。
你也可以使用initWithData:encoding: 和 dataUsingEncoding: 等方法来和NSData交互。

三、格式化字符串:
%@格式化符是为对象服务的,如果其响应descriptionWithLocale:消息,否则他发送description消息。
%1.2f 表示格式化一个double,带2个小数点。

NSNumber *number=@1234;
NSDictionary *dict=@{[NSDate data]:@"date"};

%C  表示Unicode, 例如, NSString *s = [NSString stringWithFormat:@"Long %C dash", 0x2014];
也可以使用
NSString *s=[NSString stringWithUTF8String:@"Long \xe2\x80\x94 dash"];

NSLog和NSLogv:

四、格式化符:
%@  descriptionWithLocale:  => description  ,也工作于CFTypeRef对象,返回CFCopyDescription的结果。
%%  表示%字符
%d %D  表示32位   int
%u  %U  表示32位  unsigned int
%x  表示32位 unsigned int,并用16进制的形式表示 a-f
%X 表示32位  unisigned int,并用16进制表示   A-F
%o %O  32位 unsigned int ,8进制表示
%f  表示64位 double
%e 64位double 使用科学计数法表示 ,e
%E  同上,E
%g 64位double,在指数小于-4或大于4时同%e,否则同%f
%G 同上,%E
%c  表示8位unsigned char,作为ASCII码,或者如果它不是一个ASCII码,使用\\ddd或Unicode16进制格式\\udddd
%C  16位的unichar, 其他同上。
%s   8位  没看懂,好像是表示编码,官方建议不要用,而是显式指定编码。
%S  16位  同上
%p   表示空指针 (void *),以16进制表示,以0x开头,10-15用a-f表示。
%a  64位double,科学计数法表示,以0x开头和。。。
%A  同上,以0X开头
%F  64位double,以科学计数法表示。

长度修饰符
h       修饰遵循 d,o,u,x,或X
hh     修饰遵循d o u x 的 signed char 或 unsigned char
l         修饰 long 或 unsigned long
ll,q     long long,或 unsigned long long
L     long double
z         
t
j


基于平台的NSInteger、NSUInteger、CGFloat和CFIndex在32位和64位的环境中来提供一个一致的方式代表值。在32位环境中,NSInteger和NSUInteger表示int和unsigned int,相对地,在64位环境中,NSInteger和NSUInteger表示long和unsigned long。为了避免在不同的平台使用不同的格式符,你可以使用下面的修饰符

类型               格式符修饰符           Considerations
NSInteger       %ld或%lx                 转换值到long
NSUInteger      %lu或%lx                  转换到unsigned long
CGFloat         %f或%g                 
CFIndex         %ld或%lx                 同于NSInteger
pointer           %p或%zx

例子:
NSInteger i = 42;
printf("%ld\n", (long)i);

CGFloat imageWidth;
double tmp;
sscanf (str, "%lf", &tmp);
imageWidth = tmp;

五、从文件和URL读和写字符串
1,读:
stringWithContentOfFile:encoding:error:
stringWithContentsOfURL:encoding:error:
或者先得到NSData,然后在将其转换为NSString


2.从未知Encoding读取data:
如果你必须猜encoding:
1)尝试 stringWithContentsOfFile:useEncoding:error: 或 initWithContentsOfFile:useEncoding:error: 这些方法尝试确定encoding,如果成功就返回。
2)如果(1)失败了,尝试使用UTF-8读取
3)如果(2)失败了,尝试一个合法的encoding。
4)最后,你可以尝试NSAttributedString的加载方法 initWithURL:options:documentAttributes:error:

3,写:
writeToFile:atomically:encoding:error:和 writeToURL:atomically:encoding:error:

六、搜索、比较和排序字符串:

1,搜索和比较方法:
1)搜索方法:
rangeOfString:   、  rangeOfString:options:    、   rangeOfString:options:range:   、  rangeOfString:options:range:locale: 
rangeOfCharacterFromSet: 、  rangeOfCharacterFromSet:options:   、  rangeOfCharacterFromSet:options:range:
2) 比较方法:
compare:  、  compare:options:  、 compare:options:range:   、 compare:options:range:locale:

如果你只想简单地确定一个字符串是否包含一个给定的pattern,你可以使用一个predicate:
BOOL match=[myPredicate evaluateWithObject:myString];

2,搜索和比较的选项:
NSCaseInsensitiveSearch    忽略大小写
NSLiteraSearch           按比特比较
NSBackwardsSearch    从后向前搜索
NSAnchoredSearch        只在range的开头或结尾搜索。
NSNumericSearch       比较数字值,例如 9.txt<20.txt<100.txt

七,段落和行:
\n , \r , \r\n  。Unicode定义了一个清晰的段落分隔符 U+2029( 在Cocoa中使用常量 NSParagraphSeparetorCharacter),和行分隔符U+2028(使用常量 NSLineSeparatorCharacter)。

通过段落分隔字符串
NSArray *arr=[myString componentsSeparatedByString:@"\n"];
然而,实际上这忽略了\r \r\n,或Unicode分隔符。
取而代之,你可以使用方法 lineRangeForRange: 或  getParagraphStart:end:contentsEnd:forRange: 等来处理不同的分隔符,如下所示:

NSString *string = /* assume this exists */;
unsigned length = [string length];
unsigned paraStart = 0, paraEnd = 0, contentsEnd = 0;
NSMutableArray *array = [NSMutableArray array];
NSRange currentRange;
while (paraEnd < length) {
    [string getParagraphStart:&paraStart end:&paraEnd contentsEnd:&contentsEnd forRange:NSMakeRange(paraEnd, 0)];
    currentRange = NSMakeRange(paraStart, contentsEnd - paraStart);
    [array addObject:[string substringWithRange:currentRange]];
}


八、字符和字母集:
你可以使用 rangeOfComposedCharactersSequencesForRange: 或 rangeOfComposedCharacterSequenceAtIndex:方法,或 CFStringGetRangeOfComposedCharactersAtIndex: 。 这些方法是干什么的。。。
使用 lowercaseString,uppercaseString,capitalizedString, compare:和其变种,rangeOfString:和其变种,rangeOfCharacterFromSet:和其变种,或他们的CFString函数。

九,字符集:
一个NSCharacterSet对象代表一系列Unicode字符。NSString和NSScanner对象使用NSCharacterSet对象来组合字符到一起来进行搜索操作。

1,基础:
类簇有2各公共方法,NSCharacterSetNSMutableCharacterSet
NSString *myString = @"some text in an NSString...";
NSCharacterSet *characterSet = [NSCharacterSet uppercaseLetterCharacterSet];
NSRange letterRange = [myString rangeOfCharacterFromSet:characterSet];
这个片段执行后,letterRange.location等于第一个N的位置。如果字符串的第一个字符是大写的S,那么letterRange.location应该是0.

2,创建Character Sets:
NSMutableCharacterSet *workingSet = [[NSCharacterSet alphanumericCharacterSet] mutableCopy];
[workingSet addCharactersInString:@";:,."];
NSCharacterSet *finalCharacterSet = [workingSet copy];

另一个例子:
UniChar chars[] = {0x000C, 0x2028};
NSString *string = [[NSString alloc] initWithCharacters:chars  length:sizeof(chars) / sizeof(UniChar)];
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:string];

3,标准的Character Sets 和 Unicode Definitions:
如 lowercaseLetterCharacterSet   、  letterCharacterSet

十、Scanners
一个NSScanner对象扫描一个字符串的字符,一般用于将其转换为数组和字符串值。
1)创建一个Scanner:
NSScanner是一个类簇,只有一个public class,就是NSScanner。一般,你实例化一个Scanner通过类方法: scannerWithString: 或 localizedScannerWithString: 。 你scan components using the scan... 方法,例如scanInt: , scanDouble: 和 scanString:intoString: 。如果你正在扫描多行,你一般通过一个while loop直到scanner遇到string的末尾。就像下面:

float aFloat;
NSScanner *theScanner = [NSScanner scannerWithString:aString];
while ([theScanner isAtEnd] == NO) {
    [theScanner scanFloat:&aFloat];
    // implementation continues...
}

你可以配置一个scanner考虑或忽略大小写,使用setCaseSensitive:方法。默认地,scanner忽略大小写。

2,使用Scanner:
例如,字符串“137 small cases of bananas”在扫描了一个整数之后,scanner的Location是3,经常你需要忽略某些你不感兴趣的字符,提过某些位置。你可以修改扫描位置 setScanLocation:方法。
你可以配置一个scanner来跳过一系列字符 setCharactersToBeSkipped:方法。
如果你想从当前位置读到指定的字符串,你可以使用 scanUpToString:intoString: (你可以传递NULL作为第二个参数,如果你只是想跳过某段字符),例如,给定下面的字符串:
137 small cases of bananas
下面的代码:
NSString *bananas = @"137 small cases of bananas";
NSString *separatorString = @" of";
NSScanner *aScanner = [NSScanner scannerWithString:bananas];
NSInteger anInteger;
[aScanner scanInteger:&anInteger];
NSString *container;
[aScanner scanUpToString:separatorString intoString:&container];

注意到搜索字符串是 " of"。默认地,scanner忽略空白符,所以在整数之后的空格字符被忽略了。然而,一旦scanner开始积累字符,所有的字符都被输出到字符串,直到搜索字符串被搜索到、因此,如果搜索字符串是"of"(前面没有空白符),container的值是"small cases "(包括后面的空格);如果搜索字符串是" of"(在前面有一个空格),contaier是"small cases"。
在扫描到一个指定字符串后,scan location 是这个字符串的开始处。如果你想扫描过这个字符串,因此你必须首先scan in the string you scanned up to. 如下所示:

[aScanner scanString:separatorString intoString:NULL];
NSString *product;
product = [[aScanner string] substringFromIndex:[aScanner scanLocation]];
// could also use:
// product = [bananas substringFromIndex:[aScanner scanLocation]];

3,例子:
NSString *string = @"Product: Acme Potato Peeler; Cost: 0.98 73\n\
Product: Chef Pierre Pasta Fork; Cost: 0.75 19\n\
Product: Chef Pierre Colander; Cost: 1.27 2\n";

NSCharacterSet *semicolonSet;
NSScanner *theScanner;
 
NSString *PRODUCT = @"Product:";
NSString *COST = @"Cost:";
 
NSString *productName;
float productCost;
NSInteger productSold;
 
semicolonSet = [NSCharacterSet characterSetWithCharactersInString:@";"];
theScanner = [NSScanner scannerWithString:string];
 
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);
    }
}


4,本地化: localizedScannerWithString:

十一、表示文件路径的字符串:
使用stringByStandardizingPath: 。这里执行了一系列任务:
1)扩展一个波浪号~表达式
2)减少空组件部分和代表当前文件夹(如//和/./)到单独路径分隔符
3)在绝对路径里,将..解释为真实父文件夹。
例如:
NSString *path = @"/usr/bin/./grep";
NSString *standardizedPath = [path stringByStandardizingPath];
// standardizedPath: /usr/bin/grep
 
path = @"~me";
standardizedPath = [path stringByStandardizingPath];
// standardizedPath (assuming conventional naming scheme): /Users/Me
 
path = @"/usr/include/objc/..";
standardizedPath = [path stringByStandardizingPath];
// standardizedPath: /usr/include
 
path = @"/private/usr/include";
standardizedPath = [path stringByStandardizingPath];
// standardizedPath: /usr/include

使用文件夹:
// Assuming that users’ home directories are stored in /Users
NSString *meHome = [@"~me" stringByExpandingTildeInPath];
// meHome = @"/Users/me"
 
NSString *mePublic = [@"~me/Public" stringByExpandingTildeInPath];
// mePublic = @"/Users/me/Public"

你可以获得当前用户或给定用户的主目录,通过 NSHomeDirectory 和 NSHomeDirectoryForUser:
NSString *currentUserHomeDirectory = NSHomeDirectory();
NSString *meHomeDirectory = NSHomeDirectoryForUser(@"me");

注意,你一般应该使用NSSearchPathForDirectoriesInDomains来定位标准的文件夹。例如:
instead of:
NSString *documentsDirectory =[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];

你应该使用:
NSString *documentsDirectory;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([paths count] > 0) {
    documentsDirectory = [paths objectAtIndex:0];
}


路径部件:
pathExtension  扩展名
stringByDeletingPathExtension 
stringByDeletingLastPathComponent 一般用于获得文件夹
lastPathComponent 一般用于获取文件名

使用这些方法,你可以得到路径的文件夹,文件名,和扩展名,如下所示:
NSString *documentPath = @"~me/Public/Demo/readme.txt";
 
NSString *documentDirectory = [documentPath stringByDeletingLastPathComponent];
// documentDirectory = @"~me/Public/Demo"
 
NSString *documentFilename = [documentPath lastPathComponent];
// documentFilename = @"readme.txt"
 
NSString *documentExtension = [documentPath pathExtension];
// documentExtension = @"txt"

File Name Completion 文件名完成:(用于搜索)
completePathIntoString:caseSensitive:matchesIntoArray:filterTypes:
例如,假定一个文件夹 ~/Demo 包含下列文件:
ReadMe.txt readme.html readme.rtf recondite.txt test.txt
你可以查找到所有可以完成 ~/Demo/r 如下所示:
NSString *partialPath = @"~/Demo/r";
NSString *longestCompletion;
NSArray *outputArray;
 
unsigned allMatches = [partialPath completePathIntoString:&longestCompletion
    caseSensitive:NO
    matchesIntoArray:&outputArray
    filterTypes:NULL];
 
// allMatches = 3
// longestCompletion = @"~/Demo/re"
// outputArray = (@"~/Demo/readme.html", "~/Demo/readme.rtf", "~/Demo/recondite.txt")

你可以限定扩展名,例如 “.txt” or “.rtf” as follows:

NSArray *filterTypes = @[@"txt", @"rtf"];
 
unsigned textMatches = [partialPath completePathIntoString:&outputName
    caseSensitive:NO
    matchesIntoArray:&outputArray
    filterTypes:filterTypes];
// allMatches = 2
// longestCompletion = @"~/Demo/re"
// outputArray = (@"~/Demo/readme.rtf", @"~/Demo/recondite.txt")

十二、绘制字符串:
drawAtPoint:withAttributes:

你可能感兴趣的:(String Programming Guide for Cocoa--字符串编程指南)