国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

云南省建設(shè)廳招標(biāo)辦網(wǎng)站網(wǎng)絡(luò)營銷活動策劃

云南省建設(shè)廳招標(biāo)辦網(wǎng)站,網(wǎng)絡(luò)營銷活動策劃,知名網(wǎng)站建設(shè)哪家好,有做學(xué)歷在網(wǎng)站能查的到的1. JSON 是什么 JSON(JavaScript Object Notation)是一個用于數(shù)據(jù)交換的文本格式,現(xiàn)時的標(biāo)準(zhǔn)為ECMA-404 。 雖然 JSON 源自于 JavaScript 語言,但它只是一種數(shù)據(jù)格式,可用于任何編程語言?,F(xiàn)時具有類似功能的格式有X…

1. JSON 是什么

JSON(JavaScript Object Notation)是一個用于數(shù)據(jù)交換的文本格式,現(xiàn)時的標(biāo)準(zhǔn)為ECMA-404 。

雖然 JSON 源自于 JavaScript 語言,但它只是一種數(shù)據(jù)格式,可用于任何編程語言?,F(xiàn)時具有類似功能的格式有XML、YAML,當(dāng)中以 JSON 的語法最為簡單。

例如,一個動態(tài)網(wǎng)頁想從服務(wù)器獲得數(shù)據(jù)時,服務(wù)器從數(shù)據(jù)庫查找數(shù)據(jù),然后把數(shù)據(jù)轉(zhuǎn)換成 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": "Person Education","Country": "India"},"website": null
}

網(wǎng)頁的腳本代碼就可以把此 JSON 文本解析為內(nèi)部的數(shù)據(jù)結(jié)構(gòu)去使用。

從此例子可看出,JSON 是樹狀結(jié)構(gòu),而 JSON 只包含 6 種數(shù)據(jù)類型:

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

我們要實現(xiàn)的 JSON 庫,主要是完成 3 個需求:

  1. 把 JSON 文本解析為一個樹狀數(shù)據(jù)結(jié)構(gòu)(parse)。
  2. 提供接口訪問該數(shù)據(jù)結(jié)構(gòu)(access)。
  3. 把數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換成 JSON 文本(stringify)。

我們會逐步實現(xiàn)這些需求。在本單元中,我們只實現(xiàn)最簡單的 null 和 boolean 解析。

2. 搭建編譯環(huán)境

源代碼位于 kikajson,當(dāng)中 01 為本單元的代碼。

代碼文件只有 3 個:

  1. kikajson.h:leptjson 的頭文件(header file),含有對外的類型和 API 函數(shù)聲明。
  2. kikajson.c:leptjson 的實現(xiàn)文件(implementation file),含有內(nèi)部的類型聲明和函數(shù)實現(xiàn)。此文件會編譯成庫。
  3. test.c:我們使用測試驅(qū)動開發(fā)(test driven development, TDD)。此文件包含測試程序,需要鏈接 leptjson 庫。

為了方便跨平臺開發(fā),使用一個軟件配置工具 CMake。

使用 Visual Studio 打開本單元項目,VS會自動進行CMake的配置(前提是已經(jīng)VS已經(jīng)安裝好相應(yīng)的插件),然后點開 CMakeLists.txt,運行當(dāng)前文檔,就可以編譯運行了

編譯運行后會出現(xiàn):

D:\rep\kikajson\test.c:56: expect: 3 actual: 0
11/12 (91.67%) passed

若看到類似以上的結(jié)果,說明已成功搭建編譯環(huán)境,可以去看看那幾個代碼文件的內(nèi)容了。

3. 頭文件與 API 設(shè)計

C 語言有頭文件的概念,需要使用 #include去引入頭文件中的類型聲明和函數(shù)聲明。但由于頭文件也可以 #include 其他頭文件,為避免重復(fù)聲明,通常會利用宏加入 include 防范(include guard):

#ifndef KIKAJSON_H__
#define KIKAJSON_H__/* ... */#endif /* KIKAJSON_H__ */

宏的名字必須是唯一的,通常習(xí)慣以 _H__ 作為后綴。由于 leptjson 只有一個頭文件,可以簡單命名為 KIKAJSON_H__。如果項目有多個文件或目錄結(jié)構(gòu),可以用 項目名稱_目錄_文件名稱_H__ 這種命名方式。

如前所述,JSON 中有 6 種數(shù)據(jù)類型,如果把 true 和 false 當(dāng)作兩個類型就是 7 種,我們?yōu)榇寺暶饕粋€枚舉類型(enumeration type):

typedef enum { KIKA_NULL, KIKA_FALSE, KIKA_TRUE, KIKA_NUMBER, KIKA_STRING, KIKA_ARRAY, KIKA_OBJECT } kika_type;

因為 C 語言沒有 C++ 的命名空間(namespace)功能,一般會使用項目的簡寫作為標(biāo)識符的前綴。通常枚舉值用全大寫(如 KIKA_NULL),而類型及函數(shù)則用小寫(如 kika_type)。

接下來,我們聲明 JSON 的數(shù)據(jù)結(jié)構(gòu)。JSON 是一個樹形結(jié)構(gòu),我們最終需要實現(xiàn)一個樹的數(shù)據(jù)結(jié)構(gòu),每個節(jié)點使用 kika_value 結(jié)構(gòu)體表示,我們會稱它為一個 JSON 值(JSON value)。
在此單元中,我們只需要實現(xiàn) null, true 和 false 的解析,因此該結(jié)構(gòu)體只需要存儲一個 kika_type。之后的單元會逐步加入其他數(shù)據(jù)。

typedef struct {kika_type type;
}kika_value;

C 語言的結(jié)構(gòu)體是以 struct X {} 形式聲明的,定義變量時也要寫成 struct X x;。為方便使用,上面的代碼使用了 typedef

然后,我們現(xiàn)在只需要兩個 API 函數(shù):

(1)一個是解析 JSON:

int kika_parse(kika_value* v, const char* json);

傳入的 JSON 文本是一個 C 字符串(空字符結(jié)尾字符串/null-terminated string),由于我們不應(yīng)該改動這個輸入字符串,所以使用 const char* 類型。

另一注意點是,傳入的根節(jié)點指針 v 是由使用方負責(zé)分配的,所以一般用法是:

kika_value v;
const char json[] = ...;
int ret = kika_parse(&v, json);

返回值是以下這些枚舉值,無錯誤會返回 KIKA_PARSE_OK,其他值在下節(jié)解釋。

enum {KIKA_PARSE_OK = 0,KIKA_PARSE_EXPECT_VALUE,KIKA_PARSE_INVALID_VALUE,KIKA_PARSE_ROOT_NOT_SINGULAR
};

(2)另一個是一個訪問結(jié)果的函數(shù),就是獲取其類型:

kika_type kika_get_type(const kika_value* v);

4. JSON 語法子集

下面是此單元的 JSON 語法子集,使用 RFC7159 中的 ABNF 表示:

JSON-text = ws value ws
ws = *(%x20 / %x09 / %x0A / %x0D)
value = null / false / true 
null  = "null"
false = "false"
true  = "true"

當(dāng)中 %xhh 表示以 16 進制表示的字符,/ 是多選一,* 是零或多個,() 用于分組。

第一行的意思是,JSON 文本由 3 部分組成,首先是空白(whitespace),接著是一個值,最后是空白。

第二行的意思是,所謂空白,是由零或多個空格符(space U+0020)、制表符(tab U+0009)、換行符(LF U+000A)、回車符(CR U+000D)所組成。

第三行的意思是,我們現(xiàn)時的值只可以是 null、false 或 true。

第四到六行指出了 null、false、 true 對應(yīng)的字面值(literal)。

我們的解析器應(yīng)能判斷輸入是否一個合法的 JSON。如果輸入的 JSON 不合符這個語法,我們要產(chǎn)生對應(yīng)的錯誤碼,方便使用者追查問題。

在這個 JSON 語法子集下,我們定義 3 種錯誤碼:

  • 若一個 JSON 只含有空白,傳回 KIKA_PARSE_EXPECT_VALUE。
  • 若一個值之后,在空白之后還有其他字符,傳回 KIKA_PARSE_ROOT_NOT_SINGULAR。
  • 若值不是那三種字面值,傳回 KIKA_PARSE_INVALID_VALUE。

5. 單元測試

許多初學(xué)者在做編程練習(xí)時,都是以 printfcout 打印結(jié)果,再用肉眼對比結(jié)果是否乎合預(yù)期。但當(dāng)軟件項目越來越復(fù)雜,這個做法會越來越低效。一般我們會采用自動的測試方式,例如單元測試(unit testing)。單元測試也能確保其他人修改代碼后,原來的功能維持正確(這稱為回歸測試/regression testing)。

常用的單元測試框架有 xUnit 系列,如 C++ 的 Google Test、C# 的 NUnit。我們?yōu)榱撕唵纹鹨?#xff0c;會編寫一個極簡單的單元測試方式。

一般來說,軟件開發(fā)是以周期進行的。例如,加入一個功能,再寫關(guān)于該功能的單元測試。但也有另一種軟件開發(fā)方法論,稱為測試驅(qū)動開發(fā)(test-driven development, TDD),它的主要循環(huán)步驟是:

  1. 加入一個測試。
  2. 運行所有測試,新的測試應(yīng)該會失敗。
  3. 編寫實現(xiàn)代碼。
  4. 運行所有測試,若有測試失敗回到3。
  5. 重構(gòu)代碼。
  6. 回到 1

TDD 是先寫測試,再實現(xiàn)功能。這樣的好處是,實現(xiàn)只會剛好滿足測試,而不會寫了一些不需要的代碼,或是沒有被測試的代碼。

但無論我們是采用 TDD,或是先實現(xiàn)后測試,都應(yīng)盡量加入足夠覆蓋率的單元測試。

回到 kikajson 項目,test.c 包含了一個極簡的單元測試框架:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kikajson.h"static int main_ret = 0;
static int test_count = 0;
static int test_pass = 0;#define EXPECT_EQ_BASE(equality, expect, actual, format) \do {\test_count++;\if (equality)\test_pass++;\else {\fprintf(stderr, "%s:%d: expect: " format " actual: " format "\n", __FILE__, __LINE__, expect, actual);\main_ret = 1;\}\} while(0)#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d")static void test_parse_null() {kika_value v;v.type = KIKA_TRUE;EXPECT_EQ_INT(KIKA_PARSE_OK, kika_parse(&v, "null"));EXPECT_EQ_INT(KIKA_NULL, kika_get_type(&v));
}/* ... */static void test_parse() {test_parse_null();/* ... */
}int main() {test_parse();printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count);return main_ret;
}

現(xiàn)時只提供了一個 EXPECT_EQ_INT(expect, actual) 的宏,每次使用這個宏時,如果 expect != actual(預(yù)期值不等于實際值),便會輸出錯誤信息。

若按照 TDD 的步驟,我們先寫一個測試,如上面的 test_parse_null(),而 kika_parse() 只返回 KIKA_PARSE_OK

D:\rep\kikajson\test.c:27: expect: 0 actual: 1
1/2 (50.00%) passed

第一個測試因為 kika_parse() 返回 KIKA_PARSE_OK,所以是通過的。

第二個測試因為 kika_parse() 沒有把 v.type 改成 KIKA_NULL,造成失敗。我們再實現(xiàn) kika_parse() 令到它能通過測試。

然而,完全按照 TDD 的步驟來開發(fā),是會減慢開發(fā)進程。所以有時需要在這兩種極端的工作方式取平衡。一種做法是在設(shè)計 API 后,先寫部分測試代碼,再寫滿足那些測試的實現(xiàn)。

6. 宏的編寫技巧

關(guān)于 EXPECT_EQ_BASE 宏的編寫技巧,簡單說明一下。反斜線代表該行未結(jié)束,會串接下一行。而如果宏里有多過一個語句(statement),就需要用 do { /*...*/ } while(0) 包裹成單個語句,否則會有如下的問題:

#define M() a(); b()
if (cond)M();
elsec();/* 預(yù)處理后 */if (cond)a(); b(); /* b(); 在 if 之外     */
else          /* <- else 缺乏對應(yīng) if */c();

只用 { } 也不行:

#define M() { a(); b(); }/* 預(yù)處理后 */if (cond){ a(); b(); }; /* 最后的分號代表 if 語句結(jié)束 */
else               /* else 缺乏對應(yīng) if */c();

用 do while 就行了:

#define M() do { a(); b(); } while(0)/* 預(yù)處理后 */if (cond)do { a(); b(); } while(0);
elsec();

7. 實現(xiàn)解析器

有了 API 的設(shè)計、單元測試,終于要實現(xiàn)解析器了。

首先為了減少解析函數(shù)之間傳遞多個參數(shù),我們把這些數(shù)據(jù)都放進一個 kika_context 結(jié)構(gòu)體:

typedef struct {const char* json;
}kika_context;/* ... *//* 提示:這里應(yīng)該是 JSON-text = ws value ws */
/* 以下實現(xiàn)沒處理最后的 ws 和 KIKA_PARSE_ROOT_NOT_SINGULAR */
int kika_parse(kika_value* v, const char* json) {kika_context c;assert(v != NULL);c.json = json;v->type = KIKA_NULL;kika_parse_whitespace(&c);return kika_parse_value(&c, v);
}

暫時我們只儲存 json 字符串當(dāng)前位置,之后的單元我們需要加入更多內(nèi)容。

kika_parse() 失敗,會把 v 設(shè)為 null 類型,所以這里先把它設(shè)為 null,讓 kika_parse_value() 寫入解析出來的根值。

kikajson 是一個手寫的遞歸下降解析器(recursive descent parser)。由于 JSON 語法特別簡單,我們不需要寫分詞器(tokenizer),只需檢測下一個字符,便可以知道它是哪種類型的值,然后調(diào)用相關(guān)的分析函數(shù)。對于完整的 JSON 語法,跳過空白后,只需檢測當(dāng)前字符:

  • n ? null
  • t ? true
  • f ? false
  • " ? string
  • 0-9/- ? number
  • [ ? array
  • { ? object

所以,我們可以按照 JSON 語法一節(jié)的 EBNF 簡單翻譯成解析函數(shù):

#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0)/* ws = *(%x20 / %x09 / %x0A / %x0D) */
static void kika_parse_whitespace(kika_context* c) {const char *p = c->json;while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')p++;c->json = p;
}/* null  = "null" */
static int kika_parse_null(kika_context* c, kika_value* v) {EXPECT(c, 'n');if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')return KIKA_PARSE_INVALID_VALUE;c->json += 3;v->type = KIKA_NULL;return KIKA_PARSE_OK;
}/* value = null / false / true */
/* 提示:下面代碼沒處理 false / true,將會是練習(xí)之一 */
static int kika_parse_value(kika_context* c, kika_value* v) {switch (*c->json) {case 'n':  return kika_parse_null(c, v);case '\0': return KIKA_PARSE_EXPECT_VALUE;default:   return KIKA_PARSE_INVALID_VALUE;}
}

由于 kika_parse_whitespace() 是不會出現(xiàn)錯誤的,返回類型為 void。其它的解析函數(shù)會返回錯誤碼,傳遞至頂層。

8. 關(guān)于斷言

斷言(assertion)是 C 語言中常用的防御式編程方式,減少編程錯誤。最常用的是在函數(shù)開始的地方,檢測所有參數(shù)。有時候也可以在調(diào)用函數(shù)后,檢查上下文是否正確。

C 語言的標(biāo)準(zhǔn)庫含有 assert() 這個宏(需 #include <assert.h>),提供斷言功能。當(dāng)程序以 release 配置編譯時(定義了 NDEBUG 宏),assert() 不會做檢測;而當(dāng)在 debug 配置時(沒定義 NDEBUG 宏),則會在運行時檢測 assert(cond) 中的條件是否為真(非 0),斷言失敗會直接令程序崩潰。

例如上面的 kika_parse_null() 開始時,當(dāng)前字符應(yīng)該是 'n',所以我們使用一個宏 EXPECT(c, ch) 進行斷言,并跳到下一字符。

初使用斷言的同學(xué),可能會錯誤地把含副作用的代碼放在 assert() 中:

assert(x++ == 0); /* 這是錯誤的! */

這樣會導(dǎo)致 debug 和 release 版的行為不一樣。

另一個問題是,初學(xué)者可能會難于分辨何時使用斷言,何時使用運行時錯誤(如返回錯誤值或在 C++ 中拋出異常)。簡單的答案是,如果那個錯誤是由于程序員錯誤編碼所造成的(例如傳入不合法的參數(shù)),那么應(yīng)用斷言;如果那個錯誤是程序員無法避免,而是由運行時的環(huán)境所造成的,就要處理運行時錯誤(例如開啟文件失敗)。

9. 總結(jié)與練習(xí)

本文介紹了如何配置一個編程環(huán)境,單元測試的重要性,以至于一個 JSON 解析器的子集實現(xiàn)。以下是本單元的練習(xí),解答在 01-exercise 中。

  1. 修正關(guān)于 KIKA_PARSE_ROOT_NOT_SINGULAR 的單元測試,若 json 在一個值之后,空白之后還有其它字符,則要返回 KIKA_PARSE_ROOT_NOT_SINGULAR。
  2. 參考 test_parse_null(),加入 test_parse_true()test_parse_false() 單元測試。
  3. 參考 kika_parse_null() 的實現(xiàn)和調(diào)用方法,解析 true 和 false 值。

10. 常見問答

1… 為什么使用宏而不用函數(shù)或內(nèi)聯(lián)函數(shù)?

因為這個測試框架使用了 __LINE__ 這個編譯器提供的宏,代表編譯時該行的行號。如果用函數(shù)或內(nèi)聯(lián)函數(shù),每次的行號便都會相同。另外,內(nèi)聯(lián)函數(shù)是 C99 的新增功能,本框架使用 C89。

其他常見問答將會從評論中整理。

http://aloenet.com.cn/news/46644.html

相關(guān)文章:

  • olam wordpress搜索引擎優(yōu)化搜索優(yōu)化
  • 常德市做網(wǎng)站聯(lián)系電話cps廣告聯(lián)盟
  • wordpress注冊增加性別選項seo上首頁
  • 在工商局網(wǎng)站做年報要交費嗎百度網(wǎng)盤登錄入口 網(wǎng)頁
  • 進入百度首頁seo優(yōu)化技術(shù)廠家
  • 無人區(qū)免費電影入口seo研究中心南寧線下
  • 武漢做營銷型網(wǎng)站建設(shè)寫手接單平臺
  • 公安局門戶網(wǎng)站建設(shè)方案網(wǎng)絡(luò)營銷的功能有哪些?
  • 展架設(shè)計在哪個網(wǎng)站做泉州百度首頁優(yōu)化
  • 網(wǎng)站banner尺寸1920seo 推廣教程
  • 營銷型網(wǎng)站建設(shè)發(fā)難卡點視頻軟件下載
  • 廊坊網(wǎng)站制作公司網(wǎng)站的優(yōu)化和推廣方案
  • 深圳凡科網(wǎng)站建設(shè)廣告策劃公司
  • 網(wǎng)站建設(shè)公司在線qq客服代碼單頁網(wǎng)站怎么優(yōu)化
  • 穆棱建設(shè)局網(wǎng)站二級域名網(wǎng)址查詢
  • 家居網(wǎng)站建設(shè)的背景及意義百度網(wǎng)
  • 云南做網(wǎng)站要多少錢百度競價排名
  • 深圳租賃住房和建設(shè)局網(wǎng)站長春網(wǎng)站建設(shè)方案優(yōu)化
  • 電商公司做網(wǎng)站沈陽關(guān)鍵詞seo
  • 氧os哪個網(wǎng)站做的最好萬江專業(yè)網(wǎng)站快速排名
  • 網(wǎng)站的充值是怎么做的怎么可以讓百度快速收錄視頻
  • 新的網(wǎng)站平臺如何做地推百度關(guān)鍵詞工具在哪里
  • 青島城陽網(wǎng)站制作怎樣有效的做網(wǎng)上宣傳
  • 加盟類網(wǎng)站怎么做競價推廣賬戶競價托管費用
  • 自個網(wǎng)站媒體軟文推廣平臺
  • wordpress+Apache升級seo深圳網(wǎng)絡(luò)推廣
  • wordpress安裝云服務(wù)器紹興網(wǎng)站快速排名優(yōu)化
  • 豐鎮(zhèn)網(wǎng)站建設(shè)福州百度推廣排名
  • 一個網(wǎng)絡(luò)空間如何做兩個網(wǎng)站百度關(guān)鍵詞推廣條件
  • 網(wǎng)站建設(shè)與微店網(wǎng)絡(luò)營銷推廣及優(yōu)化方案