1. 程式人生 > >從0開始學習JSON庫(一)

從0開始學習JSON庫(一)

從零開始學習JSON庫(一)

  • LINDA
  • 2018/5/8

原始碼連結

前幾天看了葉神的知乎專欄從零開始的JSON庫教程,葉老師很詳盡地跟我們講解一個專案的正確開啟方式:
1. 要做一個專案首先要知道做什麼,即專案要實現的協議是怎麼樣的,可以先看相關的RFC文件。
2. 知道大概要做什麼了,我們先從簡單的入手,構思相應的資料結構,函式介面,羅列出來。
3. 先寫對應介面的單元測試,當然,我們不可能一下子就把所有測試都想到,先寫一部分能想到的。
4. 把相應介面實現,看測試能否通過,測試通過了,思考是否實現過於繁瑣,能不能通過重構簡化介面。在寫介面時,對於一些正確情況下不可能出現的條件要設斷言,這樣在重構時才能避免一些錯誤。
5. 測試通過後,進行下一個介面的實現,重複2-4.

我跟著老師的步驟把過程走了一遍,還是覺得應該自己從0開始實現一個JSON庫,這樣才能發現自己有哪些沒掌握,並學學老師的寫作排版。好,我要開動來(^o^)/~
注:引用為葉老師教程裡的部分原文。

JSON是什麼

JSON(JavaScript Object Notation)是一個用於資料交換的文字格式,的標準為ECMA-404。雖然 JSON 源至於 JavaScript 語言,但它只是一種資料格式,可用於任何程式語言。現時具類似功能的格式有 XML、YAML,當中以 JSON 的語法最為簡單。

例如,一個動態網頁想從伺服器獲得資料時,伺服器從資料庫查詢資料,然後把資料轉換成 JSON 文字格式:

{
    "title": "Design Patterns",
    "subtitle": "Elements of Reusable Object-Oriented Software",
    "author": [
        "Erich Gamma",
        "Richard Helm",
        "Ralph Johnson",
        "John Vlissides"
    ],
    "year": 2009,
    "weight": 1.8,
    "hardcover": true,
    "publisher": {
        "Company"
: "Pearson Education", "Country": "India" }, "website": null }

網頁的指令碼程式碼就可以把此 JSON 文字解析為內部的資料結構去使用。
從此例子可看出,JSON 是樹狀結構,而 JSON 只包含 6 種資料型別:

  • null: 表示為 null
  • boolean: 表示為 true 或 false
  • number: 一般的浮點數表示方式,在下一單元詳細說明
  • string: 表示為 “…”
  • array: 表示為 [ … ]
  • object: 表示為 { … }

我們要實現的 JSON 庫,主要是完成 3 個需求:

  1. 把 JSON 文字解析為一個樹狀資料結構(parse)。
  2. 提供介面訪問該資料結構(access)。
  3. 把資料結構轉換成 JSON 文字(stringify)。

requirement.png

JSON解析

1. null和boolean的解析

JSON文字表示(詳見老師課程1)

JSON-text = ws value ws
ws = *(%x20 / %x09 / %x0A / %x0D)
value = null / false / true 
null  = "null"
false = "false"
true  = "true"
enum{ // 返回狀態值
    LEPT_PARSE_OK = 0,  // 解析成功
    LEPT_PARSE_EXPECT_VALUE,  // 只含有空白
    LEPT_PARSE_INVALID_VALUE, // 無效值,非null,boolean字面量
    LEPT_PARSE_ROOT_NOT_SINGULAR // 若在值和空白之後還有其他字元
};
enum{ // JSON型別
    LEPT_NULL, LEPT_FALSE, LEPT_TRUE
};

class leptjson{
public:
    leptjson():type(LEPT_NULL){}
    int  parse(const string& json); // 解析json文字,目前只有null,boolean
    int  gettype();  // 獲取json型別
private:
    int parse_whitespace(); // 清除空格
    int parse_value(const string& json); // 解析分支
    int parse_null(const string& json);  // 解析null
    int parse_true(const string& json); // 解析true
    int parse_false(const string& json); // 解析false

    int type; // 型別
    size_t pos;  // 開始的位置
};

單元測試(使用google C++ test)入門文件

class leptjsontest :public ::testing::Test{
protected:
       virtual void SetUp(){}
       virtual void TearDown() {}
leptjson json; // 被測試的物件
};

TEST_F(leptjsontest, leptnull){
    EXPECT_EQ(LEPT_PARSE_OK, json.parse("null"));
    EXPECT_EQ(LEPT_NULL, json.gettype());
}

TEST_F(leptjsontest, leptboolean){
    EXPECT_EQ(LEPT_PARSE_OK, json.parse("true"));
    EXPECT_EQ(LEPT_TRUE,json.gettype());
    EXPECT_EQ(LEPT_PARSE_OK, json.parse("false"));
    EXPECT_EQ(LEPT_TRUE,json.gettype());
}

TEST_F(leptjsontest, leptinvalid){
    EXPECT_EQ(LEPT_PARSE_INVALID_VALUE, json.parse("tru"));
    EXPECT_EQ(LEPT_NULL, json.gettype());
    EXPECT_EQ(LEPT_PARSE_INVALID_VALUE, json.parse("n"));
    EXPECT_EQ(LEPT_NULL, json.gettype());
    EXPECT_EQ(LEPT_PARSE_INVALID_VALUE, json.parse("fase"));
    EXPECT_EQ(LEPT_NULL, json.gettype());
}

TEST_F(leptjsontest, leptrootnotsingular){
    EXPECT_EQ(LEPT_PARSE_ROOT_NOT_SINGULAR, json.parse("false s"));
    EXPECT_EQ(LEPT_NULL, json.gettype());
}
使用google test遇到的一些問題:
  1. 編譯原始碼
    按照文件中所述,要先編譯google test原始碼:

    ~/googletest$: cd googletest/make  #我使用make所以要用make資料夾的makefile來編譯
    ~/googletest/googletest/make$: make
    
    #執行測試
    
    ~/googletest/googletest/make$: ./sample1_unittest
  2. 寫單元測試的makefile。下面是一個模板,只要改路徑就可以了。

# A sample Makefile for building Google Test and using it in user
# tests.  Please tweak it to suit your environment and project.  You
# may want to move it to your project's root directory.
#
# SYNOPSIS:
#
#   make [all]  - makes everything.
#   make TARGET - makes the given target.
#   make clean  - removes all files generated by make.

# Please tweak the following variable definitions as needed by your
# project, except GTEST_HEADERS, which you can use in your own targets
# but shouldn't modify.

# Points to the root of Google Test, relative to where this file is.
# Remember to tweak this if you move this file.
#gtest資料夾所在路徑
GTEST_DIR = /home/googletest/googletest

# Where to find user code.
#我的測試原始碼的路徑
USER_DIR=.

# Flags passed to the preprocessor.
#預編譯的標頭檔案
CPPFLAGS+=-I $(GTEST_DIR)/include

# Flags passed to the C++ compiler.
#g++引數
CXXFLAGS+=-g -Wall -Wextra -std=c++11

# All tests produced by this Makefile.  Remember to add new tests you
# created to the list.
#你的單元測試可執行檔案的名字
TESTS=leptjson_test

# All Google Test headers.  Usually you shouldn't change this
# definition.
#注意'\'表示下一行接著上一行,和#define的'\'同理
GTEST_HEADERS=$(GTEST_DIR)/include/gtest/gtest.h \
                $(GTEST_DIR)/include/gtest/internal/*.h

# House-keeping build targets.

all : $(TESTS)

clean :
        rm -f $(TESTS) gtest.a gtest_main.a *.o

# Builds gtest.a and gtest_main.a.

# Usually you shouldn't tweak such internal variables, indicated by a
# trailing _.
GTEST_SRCS_=$(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)

# For simplicity and to avoid depending on Google Test's
# implementation details, the dependencies specified below are
# conservative and not optimized.  This is fine as Google Test
# compiles fast and for ordinary users its source rarely changes.
gtest-all.o : $(GTEST_SRCS_)
        $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
            $(GTEST_DIR)/src/gtest-all.cc

gtest_main.o : $(GTEST_SRCS_)
        $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
            $(GTEST_DIR)/src/gtest_main.cc

gtest.a : gtest-all.o
        $(AR) $(ARFLAGS) [email protected] $^

gtest_main.a : gtest-all.o gtest_main.o
        $(AR) $(ARFLAGS) [email protected] $^

# Builds a sample test.  A test should link with either gtest.a or
# gtest_main.a, depending on whether it defines its own main()
# function.

leptjson.o : $(USER_DIR)/leptjson.cc $(USER_DIR)/leptjson.h
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/leptjson.cc

test.o : $(USER_DIR)/test.cc $(USER_DIR)/leptjson.h $(GTEST_HEADERS)
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/test.cc

#[email protected]指leptjson_test   $^指冒號後面的
leptjson_test : leptjson.o  test.o gtest_main.a
#注意:-lpthread要放在gtest_main.a 之後,否則出錯
#       $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o [email protected]
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) $^ -lpthread -o [email protected]

遇到的錯誤的解答

模板
-lpthread
每一行的開頭不能為空格,而是tab鍵。
No rule to make target ‘vertex.cpp’, needed by ‘vertex.o’. Stop.
3. 只測試函式可以使用TEST(testCaseName, testName),不同的test case測試相同的物件時,可以使用test fixture,即我上面的單元測試使用的,
當使用test fixture時,我們要使用TEST_F()代替TEST()

相關推薦

0開始學習JSON

從零開始學習JSON庫(一) LINDA 2018/5/8 原始碼連結 前幾天看了葉神的知乎專欄從零開始的JSON庫教程,葉老師很詳盡地跟我們講解一個專案的正確開啟方式: 1. 要做一個專案首先要知道做什麼,即專案要實現的協議是怎麼樣的,可以先看相

0開始學習微服務

話不多說,首先介紹微服務的相關概念 所謂微服務就是將單體應用的本地呼叫改變為通過HTTP或者RPC遠端呼叫的多應用 相比於傳統單體應用的優缺點 單體應用缺點: 不同模組直接邏輯耦合性高 任何一個模組程式碼有改動時即使是不相關的模組也要重新打包部署 部署程式碼時候要

開始學習OpenCL開發架構

處理器 多媒體 c++ stl context 實驗 通用 必看 是你 同時存在 1 異構計算、GPGPU與OpenCL OpenCL是當前一個通用的由很多公司和組織共同發起的多CPU\GPU\其他芯片 異構計算(heterogeneous)的標準,它是跨平臺的。旨在充

開始學習敏捷開發

一、敏捷開發--歷史背景:   20世紀60年代:軟體作坊,軟體規模小,以作坊式開發為主;  70年代:軟體危機,硬體飛速發展,軟體規模和複雜度激增,引發軟體危機;  80年代:軟體過程控制,引入成熟生產製造管理方法,以“過程為中心”分階段來控制軟體開發(瀑布模型),一定程度上緩解了軟體危機;  90年代:

【平行計算-CUDA開發】開始學習OpenCL開發架構

本文將作為我《從零開始做OpenCL開發》系列文章的第一篇。 1 異構計算、GPGPU與OpenCL   OpenCL是當前一個通用的由很多公司和組織共同發起的多CPU\GPU\其他晶片 異構計算(heterogeneous)的標準,它是跨平臺的。旨在充分利用G

開始學習Kinect程式設計 4.20

今天是第一天開始學習Kinect 之前並沒有接觸過這個東西 1,Kinect是什麼?–百度找的 =.= Kinectfor Xbox 360,簡稱 Kinect,是由微軟開發,應用於Xbox 360 主機的周邊裝置。它讓玩家不需要手持或踩踏控制器,而是使用

【xingorg1-ui】基於vue3.00-1搭建元件環境配置與目錄規劃

npm地址 github原始碼 開篇-環境配置 環境配置: 使用vue-cli搭建專案框架,需要用vue3的話,得先把vue-cli的版本升級到vue-cli@5以上 npm install -g @vue/cli 官網相關說明: 配置預選項: 後來後悔自己加上Lin

開始學習H5應用1——V1.0版,簡單頁面滑動切換效果

可曾看見過那些在微信上轉瘋了的H5神作?好生羨慕啊,那麼從今天開始,我將從零開始學習製作H5應用,看看那麼漂亮的頁面是怎麼樣一步一步形成的。 2016年2月更新:之前很多朋友問我要原始碼,由於工作原因,一直沒來得及上傳,今天已上傳至github,點選下面的地址

開始學Linux系統

系統啟動 linux 自定義 管理 如果 level 技術 int 沒有 Linux系統:分時多用戶多任務的操作系統; Linux系統引導流程: inittab配置文件中: 定義了linux系統的運行的7個級別:從0~6 0、6:分別代表關機和重啟,不建議設置為默認的

[Python接口自動化]開始學習python自動化1:環境搭建

help ins cnblogs 文件中 ssi 空格 plugins 變量 mod 第一步:安裝python編譯環境 安裝python編譯環境之前,必須保證已安裝jdk哈,如果為安裝,請參考https://jingyan.baidu.com/article/6dad507

第一個vue項目——0到1構建vue

第一個 清晰 vue-cli 從0到1 搭建 圖片 數據 綁定 代碼 入職學霸君三周,我的第一個大需求,世界杯活動歷時兩周,剛剛終於上線。這是我第一個實踐中的vue項目,也磕磕絆絆的完成了從react的轉變。以此為記。 首先記錄下菜鳥的學習過程: (1)看個大概 因為每個活

開始Vue專案實戰-準備篇

從前參與過一個react專案的程式碼編寫,大神搭建的框架,我主要負責業務邏輯程式碼編寫,現在回想起來似乎又什麼都不會,現在為了鞏固前端知識,決定用Vue來做這個專案的移動端網站,我本人Vue是從零開始的,一邊學習一邊寫程式碼,在這裡記錄一下過程。 專案說明: 主要功能實現一個投資平臺,會員身份為融資人或投

開始學習HTML+CSS2安裝Emmet

如何在Sublime Text3中安裝Emmet外掛 方法:參考官網 https://packagecontrol.io/installation 可能遇到的問題及處理辦法 問題:在安裝時彈出這樣顯示的視窗 Error while loading PyV8 binary: e

開始學習HTML+CSS1常用工具與網站

從零開始學習HTML+CSS(1) 開始正式學習HTML+CSS了。第一次培訓具體內容講得不多,主要介紹了一些常用工具和網站。 下面是一點整理: 工具類(個人推薦向): 瀏覽器:Google Chrome 官方下載地址:https://www.google.cn/chrome/ 編輯

開始學Socket:服務端和客戶端建立

上篇我提到Socket是TCP/IP的抽象介面。所以我們直接使用就好,沒必要知其甚解。 1.開啟VS 新建專案 名稱,位置隨意 這裡名稱是Server 框架選.NET 4.5(在這篇部落格裡也無所謂) 注意引用 using System.Net; using

開始學習HTML+CSS4Flex佈局中的排列與換行

0.參考網址:https://www.runoob.com/w3cnote/flex-grammar.html 1.排列 預設值:橫向排列(從左邊開始排) div { display:flex;/*設定為flex佈局*/ flex-direction:row;/*這

開始學習HTML+CSS3

Emmet常用示例 0.以Sublime Text3為例,預設在輸入快捷程式碼後按 Ctrl+e 得到程式碼片段 下面是一些常見示例及使用方法 1. 快捷程式碼:! 功能:自動補全html標籤 注意:此處應輸入英文中的! 示例: 輸入 按 Ctrl+e 得到

Vue + Spring Boot開始搭建個人網站 之 專案前端Vue.js環境搭建

前言:         最近在考慮搭建個人網站,想了想決定採用前後端分離模式         前端使用Vue,負責接收資料         後端使用Spring Boot,負責提供前端需要的API         就這樣開啟了我邊學習邊實踐之旅 Vue環境搭建步驟:         1、安裝node.js

開始學習PYTHON3講義把Python當做計算器

《從零開始PYTHON3》第二講 上一講我們說過了如何啟動Python IDLE整合開發學習環境,macOS/Linux都可以在命令列執行idle3。Windows則從開始選單中去尋找IDLE程式的圖示。 上一講我們還見到了Python的兩種工作模式,互動模式和程式模式。 通常在一個大型的系統中,程

開始學習PYTHON3講義寫第一個程序

最有 當前 記憶 參考資料 函數調用 情況 分鐘 頁面 容易 《從零開始PYTHON3》第三講 本頁面使用了公式插件,因博客主機過濾無法顯示的表示抱歉,並建議至個人主頁查看原文。 ? 我見過很多初學者,提到編程都有一種恐懼感,起源是感覺編程太難了。其實,難的也不過是