iOS字符串处理的最佳实践

文章目录
  1. 需求
    1. 背景
    2. 分析
  2. Do it

实践出真知,对一个技术的理解关键还要看应用和实践。最近在做一个学校的教务系统手机客户端项目,后台那边拿到的数据封装极其恶心,尤其是对时间的封装。在做这个项目的过程中,会用到字符串的查询、切割、拼接,在此拿出来讲一下收获。

需求

背景

先说说需求,这里的需求是要做学生的日程管理,也就是把学生的课程数据导出并生成一个日历,在日历对应的日期上添加课程日程。那么我们看看拿到的数据是什么样子的呢。

1
2
3
4
5
6
@property (nonatomic, copy) NSString *courseid; // 课程标识
@property (nonatomic, copy) NSString *courseName; // 课程名
@property (nonatomic, copy) NSString *year; // 学年
@property (nonatomic, copy) NSString *semester; // 学期
@property (nonatomic, copy) NSString *classroom; // 教室名
@property (nonatomic, copy) NSString *courseTimes; // 上课时间

这是由JSON直转的Model,这样的一些属性构成了一门课程,所有的属性都是字符串,那么也就是说每个Model对象是一个课程,每个课程的上课时间属性包含了一个长字符串,包含了一周内该门课程所有的上课时间,举个例子,courseTimes = @"周三第5,6节{第1-16周|单周};周三第7节{第8-12周};周五第9,10节{第8-12周}",我们就要将这样的字符串上的信息,用Calendar的形式展现给用户。

所以很明确了,切字符串。

分析

我们要从这个字符串中获得什么样的信息呢?首先,要让课程显示在正确的日期中,那么我们要提取出星期几、第几周,并且要标注是单周、双周还是哪一周有课,然后要提取出该课的上课具体节次。

所以,我针对时间,封装了Model,如下。

1
2
3
4
5
6
7
@property (nonatomic, strong) NSNumber *weekday; // 星期几 1,2,3,4,5,6,7
@property (nonatomic, strong) NSArray<NSNumber *> *sequence; // 第几节课 1,2,3,4,5,6,7,8,9,10
@property (nonatomic, strong) NSNumber *startWeek; // 开始周 1,2,3,4,5,6,7...
@property (nonatomic, strong) NSNumber *endWeek; // 结束周 1,2,3,4,5,6,7...
@property BOOL oddWeek; // 单周
@property BOOL evenWeek; // 双周

对于星期几,我们可以做出一个数组,从一到七,匹配对应的文字,返回数组下标。开始周结束周、第几节课都转成数字处理,单双周由布尔值标记。每个Model对象代表一次课的上课时间。

Do it

由于分号;在整个字符串中是唯一的,那么我们可以先用分号;作为分割符,将这个字符串分割称为几个子串:

1
2
3
4
NSArray *timeSubstrings = [course.courseTimes componentsSeparatedByString:@";"];
for (NSString *timeSubstring in timeSubstrings) {
// 在此我们拿到了每个上课时间子串
}

对于切割出的每一个子串,都是一次上课时间,那么我们可以在每次循环中,alloc出一个时间Model对象。

1
CourseTime *courseTime = [[CourseTime alloc] init];

接下来为了便于操作,我们可以用同样的方法把字符串从『节』的位置切割成两个子串。

然后换个新花样,用正则表达式,匹配出周几,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSArray *chineseWeekdays = @[@"一", @"二", @"三", @"四", @"五", @"六", @"日"];
NSRegularExpression *weekdayRegEx = [NSRegularExpression regularExpressionWithPattern:@"周.第" options:NSRegularExpressionCaseInsensitive error:&error];
if (!error) {
NSTextCheckingResult *weekResult = [weekdayRegEx firstMatchInString:jieSubstring[0] options:0 range:NSMakeRange(0, [jieSubstring[0] length])];
if (weekResult) {
NSString *chineseWeekdayResult = [timeSubstring substringWithRange:NSMakeRange(weekResult.range.location+1, 1)];
for (int i=0; i<7; ++i) {
if ([chineseWeekdays[i] isEqualToString:chineseWeekdayResult]) {
courseTime.weekday = [NSNumber numberWithInt:i+1];
break;
}
}
}
}

在这段代码中,我们用到了正则表达式周.第来匹配我们需要的第几周,再对返回的range做一些调整,即可直接获取到一二三四这个数字,再对我们之前准备的中文数字数组进行遍历匹配,匹配到的数组+1就是我们要的。

接下来我们寻找课程节次,节次都是两个数字,中间用……敲豆麻袋!节次很奇葩,居然有一个数字的情况,想想其实是经常有一门课上三节连课的情况,教务系统直接把第三节课做成了单独一次课的形式,所以我们这里要考虑可能是一节课,也可能是两节课的情况,代码如下:

1
2
3
4
5
6
7
8
if ([jieSubstring[0] rangeOfString:@","].location != NSNotFound) {
NSArray *lessonStrs = [jieSubstring[0] componentsSeparatedByString:@","];
NSString *firstLessonStr = [lessonStrs[0] substringFromIndex:3];
NSString *secondLessonStr = lessonStrs[1];
courseTime.sequence = @[[NSNumber numberWithInteger:firstLessonStr.integerValue], [NSNumber numberWithInteger:secondLessonStr.integerValue]];
} else {
courseTime.sequence = @[[NSNumber numberWithInteger:[jieSubstring[0] substringFromIndex:3].integerValue]];
}

这段代码第一行先去找两个数字之间的那个逗号,如果能够找到,那么在这里切割一下,切割后的前子串的最后一个……敲豆麻袋,如果是第十节课第十一节课呢,两位数字,所以我们用substringFromIndex:方法取后几个字符,这样一定可以取到所有数字;如果没有找到两个数字之间的逗号,说明我们不需要再切割,这里只有一节课,直接取字符即可。

之后就是单双周分割的问题了,利用刚刚的思路,我们可以通过寻找『|』字符来判定是否有单双周标记,然后取出单/双字判定,对Model里响应的布尔量做修改即可。还有周数,我们找到『-』,再取出其前后字符即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if ([jieSubstring[1] rangeOfString:@"|"].location != NSNotFound) {
NSArray *parityStrs = [jieSubstring[1] componentsSeparatedByString:@"|"];
// 单双周
if ([[parityStrs[1] substringWithRange:NSMakeRange(0, 1)] isEqualToString:@"单"]) {
courseTime.oddWeek = YES;
courseTime.evenWeek = NO;
} else {
courseTime.oddWeek = NO;
courseTime.evenWeek = YES;
}
NSArray *dashStrs = [jieSubstring[1] componentsSeparatedByString:@"-"];
courseTime.startWeek = [NSNumber numberWithInteger:[dashStrs[0] substringFromIndex:2].integerValue];
courseTime.endWeek = [NSNumber numberWithInteger:[dashStrs[1] substringWithRange:NSMakeRange(0, [dashStrs[1] length]-1)].integerValue];
} else {
// 单双周均有
courseTime.oddWeek = YES;
courseTime.evenWeek = YES;
NSArray *dashStrs = [jieSubstring[1] componentsSeparatedByString:@"-"];
courseTime.startWeek = [NSNumber numberWithInteger:[dashStrs[0] substringFromIndex:2].integerValue];
courseTime.endWeek = [NSNumber numberWithInteger:[dashStrs[1] substringWithRange:NSMakeRange(0, [dashStrs[1] length]-2)].integerValue];
}