文字列遍歴実装のJson元の解析(Objective-C)
24301 ワード
文字列遍歴実装のJson元の解析(Objective-C)
1.はじめに
最近Objective-C(以下objcと略称する)を学んで、Json解析器を作って知識を固めて、ついでに手触りを練習します.OBjcのCocoaライブラリにNSJSONSSerializationツールクラスがあり、このクラスのapiで簡単にjson解析ができ、まだ効率が高いそうです.しかし,ここではこのクラスで実現するのではなく,最も原始的な文字列遍歴を用いて自分で解析を行った.厳密なテストがないので、崩れやすいとか、判断が間違っているとか、お許しください.
2.着手前の注意事項
今回の項目の入力はJson文字列(NSString)、出力は解析結果(NSDictionary、NSArrayまたはNSString)です.jsonフォーマットが正しくない場合は
objcでは、文字列の読み取り文字はどのように読みますか?単純にcharAtIndexを使うだけでは、異なる符号化形式で中国語を読むときにエラーが発生する可能性があり、unicharを返してNSStringと比較することはできません.したがって、私は長さ1のサブストリングを取って文字列を遍歴します.すなわち、
このインプリメンテーションでは,再帰的に解析オブジェクトを生成する,すなわち,解析メソッドのインプリメンテーションで自分を呼び出す.主な原因は、json内のオブジェクトのvalueがオブジェクトである可能性があり、この形式は再帰的に実現するのに最も適しているからである.
3.フローチャート
4.実現特殊符号 を除去する.
入力されたjson文字列には、リターン遍歴文字列 ここから正式に解析を開始しても、本人はどのようにフォーマット検証を行ってから解析を行うか考えていないので、解析しながら検証し、jsonのフォーマットに合わないところまで解析して解析を終了します.
まず最初の非スペース文字から判断し、いくつかの可能性に分けて、
–
ここではまずトップの
次に、1つのオブジェクトが空であってもよいし、1つ以上の
そしてkeyを探して
この中に
keyを見つけたら
valueを探すにも状況を分けて議論する必要がある
key-valueを見つけたらforループを続け、次のkey-valueを探します.
–
最初に出会ったのが配列で、オブジェクトとは違う場合は、解析配列の方法をもう一つ書くしかありません.
同様に
次に文字を深く遍歴し、配列の各サブアイテムの分割を開始します.複数のサブアイテムは
次に、サブアイテムを解析します.サブアイテムには状況が必要です.オブジェクト、配列、文字列などかもしれません(またあなたたちです!)
ここまでで配列を解析するとか、コード量はオブジェクトの解析に対して少し少ないですが、やはり頭が痛いです.
–
「その他」の2つの字を見ると
5.まとめ
ここまで来れば、コードがそんなに長くても誰も見ていないので、ノートを書くつもりです.
文末にgithubを添付します
1.はじめに
最近Objective-C(以下objcと略称する)を学んで、Json解析器を作って知識を固めて、ついでに手触りを練習します.OBjcのCocoaライブラリにNSJSONSSerializationツールクラスがあり、このクラスのapiで簡単にjson解析ができ、まだ効率が高いそうです.しかし,ここではこのクラスで実現するのではなく,最も原始的な文字列遍歴を用いて自分で解析を行った.厳密なテストがないので、崩れやすいとか、判断が間違っているとか、お許しください.
2.着手前の注意事項
今回の項目の入力はJson文字列(NSString)、出力は解析結果(NSDictionary、NSArrayまたはNSString)です.jsonフォーマットが正しくない場合は
Json , 。
が出力されますobjcでは、文字列の読み取り文字はどのように読みますか?単純にcharAtIndexを使うだけでは、異なる符号化形式で中国語を読むときにエラーが発生する可能性があり、unicharを返してNSStringと比較することはできません.したがって、私は長さ1のサブストリングを取って文字列を遍歴します.すなわち、
[inputString substringWithRange: NSMakeRange(index, 1)]
のように、このようにして1文字を正確に取ることができます.このインプリメンテーションでは,再帰的に解析オブジェクトを生成する,すなわち,解析メソッドのインプリメンテーションで自分を呼び出す.主な原因は、json内のオブジェクトのvalueがオブジェクトである可能性があり、この形式は再帰的に実現するのに最も適しているからである.
3.フローチャート
4.実現
入力されたjson文字列には、リターン
、改行\r
、タブ\t
などが存在する可能性がありますので、これらの特殊文字を優先的に除去します.inputJson = [inputJson stringByReplacingOccurrencesOfString: @"
" withString: @""]; //
inputJson = [inputJson stringByReplacingOccurrencesOfString: @"\r" withString: @""]; //
inputJson = [inputJson stringByReplacingOccurrencesOfString: @"\t" withString: @""]; // tab
まず最初の非スペース文字から判断し、いくつかの可能性に分けて、
{ -> 、[ -> 、false 、true、null " ->
.いちいち判断が必要だ.–
{
ここではまずトップの
{}
を外してから次の解析を行います.// {}, , }
for (j = length-1; j > 0; j--) {
rightChar = [inputJson substringWithRange: NSMakeRange(j, 1)];
if ([rightChar isEqualToString: @" "]) {
continue;
}
if (![rightChar isEqualToString: @"}"]) {
return nil;
} else {
break;
}
}
inputJson = [inputJson substringWithRange: NSMakeRange( i+1, j-i-1)];
length = [inputJson length];
i = 0;
次に、1つのオブジェクトが空であってもよいし、1つ以上の
"key":value
(keyは任意の文字列であり、valueはオブジェクト、配列、数字、文字列などであってもよい)というフォーマットの文字列であってもよい.そうしないと、フォーマットは不正である.深く遍歴する前に、いくつかのflagを定義します.// rightQuote key ,leftQuote key ,hasColon key-value
// firstKeyValue key-value,metComma ,foundValue key value
BOOL rightQuote = NO, leftQuote = NO, hasColon = NO, firstKeyValue = YES, metComma = YES, foundValue = NO;
そしてkeyを探して
//
if ([leftChar isEqualToString: @" "]) {
continue;
}
//
if ( (![leftChar isEqualToString: @"\""]) && leftQuote == NO ) {
// key,
if (!firstKeyValue && [leftChar isEqualToString: @","] && metComma == NO) {
metComma = YES;
continue;
}
else {
return nil;
}
}
// key, , key if
if ([leftChar isEqualToString: @"\""] && metComma && leftQuote == NO) {
leftQuote = YES;
key = findString(inputJson, i, end);
if (key == nil) {
return nil;
}
rightQuote = YES;
i = j;
continue;
}
// ,
else if ([leftChar isEqualToString: @"\""] && !metComma){
return nil;
}
この中に
findString
関数が現れて、この関数はカスタマイズして、コードは以下の通りです//
NSString * findString (NSString * input, NSUInteger star, NSUInteger *end) // end
{
// result nil, nil
NSString * rightChar, * result = nil;
NSUInteger length = [input length];
//
for (*end = star+1; *end < length; *end+=1) {
rightChar = [input substringWithRange: NSMakeRange(*end, 1)];
if ([rightChar isEqualToString: @"\""]) {
//
if ([[input substringWithRange: NSMakeRange(*end-1, 1)] isEqualToString:@"\\"]) {
continue;
}
result = [input substringWithRange: NSMakeRange(star +1, *end- star -1)];
break;
}
}
return result;
} // end findString
keyを見つけたら
:
を探し始めます.keyの後はコロン、コロンの後はvalueと// , key json
if ( ![leftChar isEqualToString: @":"] && !hasColon) {
return nil;
}
//
if ([leftChar isEqualToString: @":"]) {
hasColon = YES;
foundValue = NO;
// value
for (i++ ; i < length; i++) {
leftChar = [inputJson substringWithRange: NSMakeRange(i, 1)];
//
if ([leftChar isEqualToString: @" "]) {
continue;
}
// , value(
foundValue = YES;
// value
...
}
// value,
if (foundValue == NO) {
return nil;
}
if (key == nil || value == nil) {
return nil;
}
// value,
[result setObject: value forKey: key];
// flag, key-value
firstKeyValue = NO; metComma = NO;
leftQuote = NO; rightQuote = NO; hasColon = NO;
//i value , for i++
i = j;
}
valueを探すにも状況を分けて議論する必要がある
//value
if ([leftChar isEqualToString: @"\""]) {
value = findString(inputJson, i, end);
if (value == nil) {
return nil;
}
break;
}
//value
if ([leftChar isEqualToString: @"["]) {
// "]" ,findCorrespondBracket
int bracketCount = findCorrespondBracket(inputJson, @"[", @"]", i, end);
if (bracketCount != 0) { //
return nil;
}
//
value = [VJsonPaser parseToArray: [inputJson substringWithRange: NSMakeRange(i, j-i+1)]];
break;
}
//value
if ([leftChar isEqualToString: @"{"]) {
int bracketCount = findCorrespondBracket(inputJson, @"{", @"}", i, end);
if (bracketCount != 0) { //
return nil;
}
value = [VJsonPaser parseToDictionary: [inputJson substringWithRange:
NSMakeRange(i, j-i+1)]];
break;
}
// ,checkOtherLegal ,
value = checkOtherLegal(inputJson, i, end, leftChar);
if (value != nil) {
break;
}
// value
return nil;
findCorrespondBracket
はカスタムメソッドであり、以下のように実現される.//
int findCorrespondBracket(NSString * input, NSString* leftBracket, NSString* rightBracket, NSUInteger star, NSUInteger* end)
{
int bracketCount = 1;
NSString * rightChar;
NSUInteger length = [input length];
for( *end = star+1; *end < length; *end+=1) {
rightChar = [input substringWithRange: NSMakeRange(*end, 1)];
// , ,
if ([rightChar isEqualToString: @"\""]) {
star = *end;
NSString* str = findString(input, star, end);
if (str == nil) {
return -1;
}
continue;
}
if ([rightChar isEqualToString: leftBracket]) {
bracketCount ++;
}
else if ([rightChar isEqualToString: rightBracket]) {
bracketCount --;
if (bracketCount == 0) {
// j value
break;
}
}
}
return bracketCount;
} // end findCorrespondBracket
checkOtherLegal
関数には、null
、true
、または666.66
のような他の雑砕状況の検証が含まれており、スコア状況の議論も含まれている.//
NSString* checkOtherLegal(NSString* input, NSUInteger star, NSUInteger* end, NSString* leftChar)
{
NSString *value = nil, *rightChar;
NSUInteger length = [input length];
//null
if ([leftChar isEqualToString: @"n"]) {
if (length < star+4) {
return nil;
}
leftChar = [input substringWithRange: NSMakeRange(star, 4)];
if (![leftChar isEqualToString: @"null"]) {
return nil;
}
value = @"null";
*end = star+3;
}
//true
else if ([leftChar isEqualToString: @"t"]) {
if (length < star+4) {
return nil;
}
leftChar = [input substringWithRange: NSMakeRange(star, 4)];
if (![leftChar isEqualToString: @"true"]) {
return nil;
}
value = @"true";
*end = star+3;
}
//false
else if ([leftChar isEqualToString: @"f"]) {
if (length < star+5) {
return nil;
}
leftChar = [input substringWithRange: NSMakeRange(star, 5)];
if (![leftChar isEqualToString: @"false"]) {
return nil;
}
value = @"false";
*end = star+4;
}
//
NSPredicate* numberPredicate = [NSPredicate predicateWithFormat: @"SELF IN {'0','1','2','3','4','5','6','7','8','9'}"];
if ([numberPredicate evaluateWithObject: leftChar]) {
*end = star+1;
// 0 , , ,
if ([leftChar isEqualToString: @"0"]) {
rightChar = [input substringWithRange: NSMakeRange(*end, 1)];
if (![rightChar isEqualToString: @"."] && ![rightChar isEqualToString:@","] && ![rightChar isEqualToString:@" "]) {
return nil;
}
}
BOOL alreadyDot = NO; //
for( ; *end < length; *end += 1) {
rightChar = [input substringWithRange: NSMakeRange(*end, 1)];
if ([rightChar isEqualToString: @"."]) {
//
if (alreadyDot) {
return nil;
}
alreadyDot = YES;
//
if (length <= *end+1) {
return nil;
}
rightChar = [input substringWithRange: NSMakeRange(*end+1, 1)];
if (![numberPredicate evaluateWithObject: rightChar]) {
return nil;
}
continue;
}
//
else if ([numberPredicate evaluateWithObject:rightChar]) {
continue;
}
// value
else if ([rightChar isEqualToString:@","] || [rightChar isEqualToString:@" "]) {
break;
}
//
else {
return nil;
}
}
value = [input substringWithRange: NSMakeRange(star, *end-star)];
*end -= 1; //
}
return value;
} // end checkOtherLegal
key-valueを見つけたらforループを続け、次のkey-valueを探します.
–
[
最初に出会ったのが配列で、オブジェクトとは違う場合は、解析配列の方法をもう一つ書くしかありません.
同様に
[]
を先に取り除く// [], , ]
for (; j > 0; j--) {
rightChar = [inputString substringWithRange: NSMakeRange(j, 1)];
if ([rightChar isEqualToString: @" "]) {
continue;
}
if (![rightChar isEqualToString: @"]"]) {
return nil;
} else {
break;
}
}
inputString = [inputString substringWithRange: NSMakeRange( i+1, j-i-1)];
length = [inputString length];
次に文字を深く遍歴し、配列の各サブアイテムの分割を開始します.複数のサブアイテムは
,
によって分割されるため、,
に対していくつかの特判を先に行う必要があります.//
if ([leftChar isEqualToString: @" "]) {
continue;
}
//
if (!firstObject && [leftChar isEqualToString: @","]) {
if (metComma == YES) {
return nil;
}
metComma = YES;
//
foundObject = NO;
continue;
}
else if (firstObject && [leftChar isEqualToString: @","]) {
return nil;
}
次に、サブアイテムを解析します.サブアイテムには状況が必要です.オブジェクト、配列、文字列などかもしれません(またあなたたちです!)
NSObject *obj = nil;
if ([leftChar isEqualToString: @"{"] && metComma) {
//
int bracketCount = findCorrespondBracket(inputString, @"{", @"}", i, end);
if (bracketCount != 0) {
return nil;
}
obj = [VJsonPaser parseToDictionary:
[inputString substringWithRange: NSMakeRange(i, j-i+1)]];
}
else if ([leftChar isEqualToString: @"["] && metComma) {
//
int bracketCount = findCorrespondBracket(inputString, @"[", @"]", i, end);
if (bracketCount != 0) {
return nil;
}
obj = [VJsonPaser parseToArray: [inputString substringWithRange: NSMakeRange(i, j-i+1)]];
}
// ,
else if ([leftChar isEqualToString: @"\""] && metComma) {
obj = findString(inputString, i, end);
}
// ,
else {
obj = checkOtherLegal(inputString, i, end, leftChar);
}
if (obj == nil) {
return nil;
}
[result addObject:obj];
// flag,
foundObject = YES;
firstObject = NO;
metComma = NO;
i = j;
ここまでで配列を解析するとか、コード量はオブジェクトの解析に対して少し少ないですが、やはり頭が痛いです.
–
「その他」の2つの字を見ると
checkOtherLegal
を使うことがわかります.幸いにも1つの関数にカプセル化されていて、そんなに長いコードをコピーする必要はありません.else if ([leftChar isEqualToString: @"\""]) {
return findString(inputJson, i, end);
}
else {
NSString* str = checkOtherLegal(inputJson, i, end, leftChar);
if (str == nil) {
return nil;
}
return str;
}
5.まとめ
ここまで来れば、コードがそんなに長くても誰も見ていないので、ノートを書くつもりです.
文末にgithubを添付します