Demo集合从零开始——我的OC学习之路

2018-06-05 项目1:JSON解析器(自己实现)

2018-06-05  本文已影响55人  肠粉白粥_Hoben

一.项目要求

二.设计思路

整个程序的设计思路如下:


设计流程图
  1. 将一个NSArray放入到总解析器当中,当识别到为字典时,则使用字典解析器解析该字典,当识别到为字符时,则使用字符解析器解析该字符。
  2. 当字典解析器获得的Object里带有字符,则将Object传递给总解析器,等待字符的返回,放入字典中。
  3. 当字符解析器里的元素带有字典,则将该元素传递给总解析器,等待字典的返回后,放入数组中。
  4. 当总解析器获得了字典和字符的返回时,如果还没结束读取,则继续读取字符串,如果结束读取,则将返回结果返回主函数

三.代码实现

1.读取文件与处理

我将文件的每一个字符都读出来,并舍弃掉其中的换行符和空格,放进了一个数组dict里面

while (fgets(word, 200, jsonFile)) {
        //获取文本内容
        NSString *tmp = [NSString stringWithUTF8String: word];
        [_jsonString appendString: tmp];
        //去掉所有空格和换行
        tmp = [tmp stringByReplacingOccurrencesOfString:@" " withString:@""];
        tmp = [tmp stringByReplacingOccurrencesOfString:@"\n" withString:@""];
        tmp = [tmp stringByReplacingOccurrencesOfString:@"\t" withString:@""];
        tmp = [tmp stringByReplacingOccurrencesOfString:@"\r" withString:@""];
        for (int i = 0; i < [tmp length]; i++) {
            //转成一个个字符读取
            NSString *subChar = [tmp substringWithRange:NSMakeRange(i, 1)];
            [dict addObject: subChar];
        }
}

2.字符解析器

字符解析器通过循环遍历每一个字符,定义两个伪指针begin和end,当begin和end相遇的时候,遍历结束。
字符解析器需要处理的数据有:String、int、float、bool、null、数组,通过传入的第一个字符来判断进入哪个处理步骤

 //处理String
if ([beginChar isEqualToString: @"\""]) {
        //从开始的指针找
        begin++;
        NSInteger quoteIndex = [array indexOfObject: @"\"" inRange: NSMakeRange(begin, end - begin + 1)];
        if (quoteIndex == NSNotFound) {
            //没找到引号
            return nil;
        }
        //获得key
        NSMutableString *key = [[NSMutableString alloc] init];
        for (int i = begin; i < quoteIndex; i++) {
            [key appendString: array[i]];
        }
        begin = (int) quoteIndex + 1;
        return key;
    }

处理null、true、false即判断其是否为预设的字符串。

else if ([beginChar isEqualToString: @"n"]) {
        //处理null
        if (begin + 3 <= end) {
            if ([array[begin + 1] isEqualToString: @"u"] &&
                [array[begin + 2] isEqualToString: @"l"] &&
                [array[begin + 3] isEqualToString: @"l"]) {
                begin += 4;
                return [NSNull null];
            }
            else
                return nil;
        }
        else
            return nil;
    }
    else if ([beginChar isEqualToString: @"t"]) {
        //处理true
        if (begin + 3 <= end) {
            if ([array[begin + 1] isEqualToString: @"r"] &&
                [array[begin + 2] isEqualToString: @"u"] &&
                [array[begin + 3] isEqualToString: @"e"]) {
                begin += 4;
                return [NSNumber numberWithBool: YES];
            }
            
            else
                return nil;
        }
        else
            return nil;
    }
    else if ([beginChar isEqualToString: @"f"]) {
        //处理false
        if (begin + 4 <= end) {
            if ([array[begin + 1] isEqualToString: @"a"] &&
                [array[begin + 2] isEqualToString: @"l"] &&
                [array[begin + 3] isEqualToString: @"s"] &&
                [array[begin + 4] isEqualToString: @"e"]) {
                begin += 5;
                return [NSNumber numberWithBool: NO];
            }
            
            else
                return nil;
        }
        else
            return nil;
    }

处理数字的时候,需要用NSScanner判断数字是int还是float类型,然后再进行相应的操作。

//判断是否为整形:

- (BOOL)isPureInt:(NSString*)string{
    NSScanner* scan = [NSScanner scannerWithString:string];
    int val;
    return[scan scanInt:&val] && [scan isAtEnd];
}

//判断是否为浮点形:

- (BOOL)isPureFloat:(NSString*)string{
    NSScanner* scan = [NSScanner scannerWithString:string];
    float val;
    return[scan scanFloat:&val] && [scan isAtEnd];
}
else if ([numberArray containsObject: beginChar]) {
        //处理数字字符串
        NSMutableString *key = [[NSMutableString alloc] init];
        while ([numberArray containsObject: beginChar] || [beginChar isEqualToString: @"."]) {
            //获取数字字符串
            [key appendString: beginChar];
            begin++;
            if (begin <= end) {
                beginChar = [NSString stringWithString: array[begin]];
            }
            else
                break;
        }
        //处理数字
        if ([self isPureInt: key]) {
            int value = [key intValue];
            return [NSNumber numberWithInt: value];
        }
        else if ([self isPureFloat: key]){
            float value = [key floatValue];
            return [NSNumber numberWithFloat: value];
        }
        else {
            return nil;
        }
    }

处理数组是最考验能力的,因为数组里面的逗号、花括号、方括号,都是截然不同的几种情况。
首先遇到数组的时候,需要判断两个指针是否相遇,当相遇的时候(即begin==end),则这次数组才是真正的处理完毕。

[a,[b,c],d]

很明显,第一个逗号和第三个逗号都需要加到result数组当中去,而第二个逗号是属于子数组[b,c]的,那么当我们判断出这个逗号不属于这个result数组的时候,我们就直接把整个[b,c]取出来,解析后(交给自己递归)返回一个结果,放进result数组里面。
再来个复杂点的情况就是:

{
    "A": ["a", {
        "c": 1,
        "d": [2,3,4]
    }, "e"]
}

在这里,第一个逗号和最后一个逗号的数组是result数组的,第二个逗号属于子字典,第三、四个逗号均属于子字典里面的子数组。很复杂对吧。
所以我们的解决方案就是,检索出每一种情况,对其进行相应的操作。情况的分布基于字典、数组和逗号的位置分布,我来列几种情况:
由于正确的json格式下,右括号一定在左括号右边,因此,在右方我们只判断右括号,在左方我们只判断左括号就OK了。

//quote < left_bracket < left_brace
,[]{}
//right_bracket < quote < left_bracket 
[],{}
//right_bracket < right_brace < quote
[]{},
//braceNotFound、right_bracket < quote
[],
//bracketNotFound、right_brace < quote
{},
//left_brace < quote < right_brace < left_bracket
{,}[]
//left_brace < left_bracket < right_bracket < quote < right_brace
{[],}
//left_bracket < quote < right_bracket  < left_brace < right_brace 
[,]{}
//left_bracket < left_brace < right_brace < quote < right_bracket
[{},]

那么其实我们要获得这五个元素的index进行相应的判断就可以了。但是这里要极其小心,因为要同时考虑截取的数组长度、指针位移距离等因素。

NSArray* subString = [array subarrayWithRange: NSMakeRange(begin, end - begin + 1)];
NSInteger quoteIndex = [subString indexOfObject: @","];
NSInteger leftBracketsIndex = [subString indexOfObject: @"["];
NSInteger leftBraceIndex = [subString indexOfObject: @"{"];

获取右括号的时候一定要注意是从当前位置获取,否则的话获取到的是其他数组的括号,就会报错。

- (int) findRightBracketIndex: (NSArray* ) array
{
    int count = 0;
    for (int i = 0; i < [array count]; i++) {
        if ([array[i] isEqualToString: @"["])
            count++;
        if ([array[i] isEqualToString: @"]"]) {
            count--;
            //找到了最外层的数组
            if (count == 0)
                return i;
        }
    }
    return -1;
}

当我们截取了一个子元素之后,还要再看看下一位是不是真的是逗号或者结束了。

//看看下一位是不是逗号或者结束
if ([array[begin] isEqualToString: @","] || [array[begin] isEqualToString: @"}"]) {
          begin++;
}

处理完之后,我们应该会获得一个带元素的数组,这时候我们交由总解析器去继续解析:

id jsonResult = nil;
jsonResult = [self parseWithJsonStringArray: subArray];
if (jsonResult == nil)
     return nil;
[result addObject: jsonResult];

当然,我们还得应对来自字典解析器给我们发来的字典请求,我们也交给总解析器去处理这件事情:

else if ([beginChar isEqualToString: @"{"]) {
        id jsonResult = nil;
        jsonResult = [self parseWithJsonStringDictionary: array];
        if (jsonResult == nil)
            return nil;
        [result addObject: jsonResult];
        return result;
    }

3.字典解析

我们的字典是有格式要求的,必须为"key":value形式,其中,value可以为字典,也可以为元素。
首先来自总解析器的请求中,"key":的格式是要限制死的,我们通过寻找引号来一位位地读取出key,再次强调要注意指针的移动:

NSInteger quoteIndex = [array indexOfObject: @"\"" inRange: NSMakeRange(begin, end - begin + 1)];
if (quoteIndex == NSNotFound) {
    //没找到引号
    return nil;
}
for (int i = begin; i < quoteIndex; i++) {
    [key appendString: array[i]];
}
//找到了key
//再次右移
begin = (int) quoteIndex + 1;

然后判断跟着的是否为冒号:

if ([beginChar isEqualToString: @":"]) 
     begin++;

最后判断value是字典还是元素,如果是字典,则交由总解析器处理;如果是元素,则交由字符解析器去处理,最后返回一个result的NSDictionary类。

if ([beginChar isEqualToString: @"{"]) {
    //是一个字典
    NSMutableArray *father = [[NSMutableArray alloc] init];
    father = [self parseWithJsonStringDictionary: subArray];
    if (father == nil)
        return nil;
    [result setObject: father forKey: key];
}
else {
    //是一个元素
    NSMutableArray *father = [[NSMutableArray alloc] init];
    father = [self parseWithJsonStringArray: subArray];
    if (father == nil)
        return nil;
    [result setObject: father forKey: key];
}

4.总解析器

总解析器的作用是根据自己想要处理的字符,分成一个个的单独的字典,交由字典解析器调用。调用完毕得到返回的数据之后,将其存入result数组里面。
首先判断是不是一个合格的{}格式

if (![beginChar isEqualToString: @"{"] || ![endChar isEqualToString: @"}"])
        return nil;

然后又进行上面讨论过的各种判断,加入到result数组当中,交由字典解析器处理。

id jsonResult = nil;
jsonResult = [self parseDictionary: subDictionary];
if (jsonResult == nil)
    return nil;
[result addObject: jsonResult];

最后在返回里面判断Array的count,如果为1则只有一个字典,返回字典,否则返回数组

if ([result count] > 1)
    return result;
else
    return result[0];

最终在main函数调用就OK啦,和上一次的博客一样的。

四.测试样例

正确的输入样例:

//样例1
{
    "A": {
        "B": {
            "C": 1
        },
        "D": ["a", [true, false], "b"]
    },
    "E": 2,
    "F":{
        "G":[6.1,7.23,8.45]
    }
}
//输出1
The result dictionary is (
        {
        A =         (
                        {
                B =                 {
                    C = 1;
                };
            },
                        {
                D =                 (
                    a,
                                        (
                        1,
                        0
                    ),
                    b
                );
            }
        );
    },
        {
        E = 2;
    },
        {
        F =         {
            G =             (
                "6.1",
                "7.23",
                "8.45"
            );
        };
    }
)
//样例2
{
    "name": "中国",
    "province": [{
        "name": "黑龙江",
        "cities": {
            "city": ["哈尔滨", "大庆"]
        }
    }, {
        "name": "广东",
        "cities": {
            "city": ["广州", "深圳", "珠海"]
        }
    }, {
        "name": "台湾",
        "cities": {
            "city": ["台北", "高雄"]
        }
    }, {
        "name": "新疆",
        "cities": {
            "city": ["乌鲁木齐"]
        }
    }]
}
//输出2
The result dictionary is (
        {
        name = "中国";
    },
        {
        province =         (
                        (
                                (
                                        {
                        name = "黑龙江";
                    },
                                        {
                        cities =                         {
                            city =                             (
                                "哈尔滨",
                                "大庆"
                            );
                        };
                    }
                )
            ),
                        (
                                (
                                        {
                        name = "广东";
                    },
                                        {
                        cities =                         {
                            city =                             (
                                "广州",
                                "深圳",
                                "珠海"
                            );
                        };
                    }
                )
            ),
                        (
                                (
                                        {
                        name = "台湾";
                    },
                                        {
                        cities =                         {
                            city =                             (
                                "台北",
                                "高雄"
                            );
                        };
                    }
                )
            ),
                        (
                                (
                                        {
                        name = "新疆";
                    },
                                        {
                        cities =                         {
                            city =                             (
                                "乌鲁木齐"
                            );
                        };
                    }
                )
            )
        );
    }
)
//样例3(很多很多的子数组)
{
  "A":[1,[2,[[3]],4],[[[5]]]]
}
//输出3
The result dictionary is {
    A =     (
        1,
                (
            2,
                        (
                                (
                    3
                )
            ),
            4
        ),
                (
                        (
                                (
                    5
                )
            )
        )
    );
}
//样例4(很多子字典)
{
    "A": {
        "B": {
            "C": 1
        },
        "D": [
            [
                [
                    [1]
                ]
            ]
        ]
    }
}
//输出4
The result dictionary is {
    A =     (
                {
            B =             {
                C = 1;
            };
        },
                {
            D =             (
                                (
                                        (
                                                (
                            1
                        )
                    )
                )
            );
        }
    );
}

错误的输入样例:

//输入1(缺少冒号)
{ 
    "A"1
}

//输入2(方括号不完整)
{ 
    "A":[12
}

//输入3(花括号不完整)
{
    "A": {
        "B": 1
    }

//输入4(String类型的Object缺少引号)
{
    "A": {
        "B": tall
    }
}

//输入5(多一个逗号)
{
    "A": 1,
    ,
    "B": 5
}

//输入6(数组中间多一个逗号)
{
  "A":[1,,2]
}

//输入7(数组结尾多一个逗号)
{
  "A":[1,2,]
}

//输入8(key缺少引号)
{
  A:1
}
//输出
JSON数据不合法!
上一篇下一篇

猜你喜欢

热点阅读