自制c++ json解释器 jsonExplorer

项目源码 https://github.com/hao297531173/jsonExplorer

说在前面的话

之前我读过一本叫做《自制编程语言》 的书,给我的启发真的很大,这本书的源码在https://github.com/hao297531173/DIYProgramLanguage

我这次项目借鉴了书中的很多编码技巧,下面我就详细介绍一下核心代码编写思路

PS:对外接口的写的不多,如果你觉得这个项目很有用的话,可以自己写接口

简单用法实例

#include <cstdio>
#include <cstring>
#include <iostream>
#include "jsonExplorer.h"
using namespace std;

void get(){
    explorer e("test.json");
    e.dump();
    char a[2][100] = {"Tony", "course"};
    char b[2][100] = {"Mary", "age"};
    Value val;
    val = e.findElement(a, 2);
    if(val.type != ERROR){
        e.traverse(val);
    }
    val = e.findElement(b, 2);
    if(val.type != ERROR){
        e.traverse(val);
    }
}

int main(){
    get();
    return 0;
}

只需要引入头文件"jsonExplorer.h"就行了,然后实例化一个explorer类,构造函数中的参数就是你要解析的json文件。

之后调用接口dump()将json文件内容存入数据结构中,使用findElement(char a[][], int length);即可找到你要找的值,

其中二维数组就是你要找的值所对应的键,由外向内寻找。

输出结果

object analysis
object analysis
list analysis
object analysis
list analysis
[
        "calculus" ,"linear algebra"
    ](NUMBER):19

 

两个主要类的定义

parser

class parser{
    const char* file;   //文件路径
    FILE *fileWrite;  //写出的文件指针
    FILE *fp;   //文件指针
    Token result;
    int line;   //标识行
    char ch;
    int tokenCount = 0;
    

    //判断token是否是关键字
    TokenType isKeyword(char a[], int length);
    //跳过空格,运行完后fp指向不为空格或者回车的字符
    void skipBlanks();
    //获取下一个token
    void getNextToken();
    //显示token
    void showToken();
    //初始化result
    void initResult();
public:
    //解析主函数(用于测试)
    void parse();
    //发送token给别的程序(给别的程序调用)
    Token pullToken();
    //构造函数(一个无参数,一个有参数)
    parser(){}
    parser(char *file){
        this->file = file;
        this->fp = fopen(this->file, "r");  //打开文件
        this->fileWrite = fopen("output.txt", "w");
        this->line = 1;
        initResult();
        ch = fgetc(fp); //先读取第一个字符
        //printf("construct : %c\n", ch);
    }
    void initParser(char *file){
        this->file = file;
        this->fp = fopen(this->file, "r");  //打开文件
        this->fileWrite = fopen("output.txt", "w");
        this->line = 1;
        initResult();
        ch = fgetc(fp); //先读取第一个字符
    }
    //析构函数,主要用来关闭文件
    ~parser(){
        fclose(this->fileWrite);
        fclose(this->fp);
    }
};

类中有一个result属性,记录当前的token(有关token的定义会在后面介绍),提供两个构造函数,一个无参数的,一个有参数的,无参数的构造函数需要使用initParser()来初始化parser对象,参数就是待解析的json文件,有参数的构造函数直接将文件名传入就行了。

parse()是测试用的接口,一次将所有的token都输出到文件中(我默认是写入output.txt中,可以在构造函数中修改)

pullToken()时候提供给别的程序的接口,一次解析一个token,返回值为Token类型,返回自身属性result

explorer

class explorer{
    Value value;    //用于存储json数据
    Value error;    //用于解析错误时返回
    Token token;    //用于存储当前token
    int num;        //用于控制每行缩进个数
    int depth;      //用于记录查询深度
    parser p;
    char *file;
    //解析花括号
    Value analysisBrace();
    //解析方括号
    Value analysisSquare();
    //递归遍历对象
    void traverseObject(Value val);
    //递归遍历列表
    void traverseList(Value val);
    /*将遍历结果输出到文件*/
    void outputObject(FILE *fp, Value val);
    void outputList(FILE *fp, Value val);
    /*通过key值递归查询*/
    Value findValue(string key,Value val);
    Value findValue(Value val, char a[][MAXSIZE], int length);
    /*修改value的值*/
    Value changeValue(string key, Value ch, Value &val);
public:
    //构造函数(a是文件名字符串)
    explorer(char *a){
        this->file = a;
        p.initParser(file);
        token = p.pullToken();  //获取第一个token
        error.type = ERROR;
        this->depth = 0;    //从0开始计数
    }
    ~explorer(){}
    //提供给外部的接口
    void dump();
    //写一个遍历函数
    void traverse();
    void traverse(Value val);
    //输出到文件
    void output(char *file,Value val);
    void output(char *file);
    //根据key值查value并且返回一个value变量
    Value findElement(string key);
    Value findElement(char key[][MAXSIZE], int length);
    //根据二维数组找Value值
    //Value findElement(char a[][]);
    //修改某个key中的value值
    //参数分别是key值,要改变的value,从那个value开始查找
    //第三个参数缺省的话就是改变类自己的value
    Value changeValueOfKey(string key, Value ch, Value &val);
    Value changeValueOfKey(string key, Value ch);
    
};

构造函数的参数是待解析的文件名。

dump()是提供给用户的接口,将json数据存入内部数据结构中,也就是类中value属性中

traverse()是遍历Value数据(关于Value的定义会在后面介绍),无参数的就是遍历类本身的value,带参数的就是遍历传入的value。

output()将Value类型的数据以json格式存入json文件中,文件名就是给定的第一个参数,同样提供写入自身value值和传入value值两个版本。

findElement()就是搜索的接口,二维数组就是要找的值所对应的键(由外向里),length就是键的个数。

主要的数据结构

在介绍算法之前,我们先来看一下我采用的数据结构

首先是Token结构体

typedef enum{
    TOKEN_UNKNOWN,  //未知类型
    
    TOKEN_LEFT_BRACE,   //左花括号
    TOKEN_RIGHT_BRACE,  //右花括号

    TOKEN_LEFT_SQUARE,  //左方括号
    TOKEN_RIGHT_SQUARE, //右方括号

    TOKEN_COMMA,        //逗号

    TOKEN_QUOTATION,   //双引号

    TOKEN_NULL,         //NULL

    TOKEN_FLAG, //斜杠
    TOKEN_RE_FLAG,   //反斜杠
    TOKEN_COLON,    //冒号
    TOKEN_EOF
}TokenType;
struct Token{
    TokenType type; //token类型
    int length; //长度
    char token[MAXSIZE];
    int lineNo; //行号
};

TokenType是一个枚举,用来表示token的类型,需要解析其他的token的时候在这里添加入其他类型即可。

length记录了token的长度

token[]记录了token的字面量

l

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值