Skip to content

Commit 79d6f6a

Browse files
authored
Merge pull request miloyip#130 from imba-tjd/patch-1
Make some improvements
2 parents 7b30f8b + 5dbd572 commit 79d6f6a

File tree

21 files changed

+104
-86
lines changed

21 files changed

+104
-86
lines changed

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
为什么选择 JSON?因为它足够简单,除基本编程外不需大量技术背景知识。JSON 有标准,可按照标准逐步实现。JSON 也是实际在许多应用上会使用的格式,所以才会有大量的开源库。
99

10-
这是一个免费、开源的教程,如果你喜欢,也可以打赏鼓励。因为工作及家庭因素,不能保证每篇文章的首发时间,请各为见谅
10+
这是一个免费、开源的教程,如果你喜欢,也可以打赏鼓励。因为工作及家庭因素,不能保证每篇文章的首发时间,请各位见谅
1111

1212
## 对象与目标
1313

@@ -44,7 +44,7 @@
4444
4. [Unicode](tutorial04/tutorial04.md)(2016/10/2 完成):Unicode 和 UTF-8 的基本知识、JSON string 的 unicode 处理。练习完成 JSON string 类型的解析。[Unicode 解答篇](tutorial04_answer/tutorial04_answer.md)(2016/10/6 完成)。
4545
5. [解析数组](tutorial05/tutorial05.md)(2016/10/7 完成):JSON array 的语法。练习完成 JSON array 类型的解析、相关内存释放。[解析数组解答篇](tutorial05_answer/tutorial05_answer.md)(2016/10/13 完成)。
4646
6. [解析对象](tutorial06/tutorial06.md)(2016/10/29 完成):JSON object 的语法、重构 string 解析函数。练习完成 JSON object 的解析、相关内存释放。[解析对象解答篇](tutorial06_answer/tutorial06_answer.md)(2016/11/15 完成)。
47-
7. [生成器](tutorial07/tutorial07.md)(2016/12/20 完成):JSON 生成过程、注意事项。练习完成 JSON 生成器。[生成器解答篇](tutorial07_answer/tutorial07_answer.md)(2017/1/5 完成)
47+
7. [生成器](tutorial07/tutorial07.md)(2016/12/20 完成):JSON 生成过程、注意事项。练习完成 JSON 生成器。[生成器解答篇](tutorial07_answer/tutorial07_answer.md)(2017/1/5 完成)
4848
8. [访问与其他功能](tutorial08/tutorial08.md)(2018/6/2 完成):JSON array/object 的访问及修改。练习完成相关功能。
4949
9. 终点及新开始:加入 nativejson-benchmark 测试,与 RapidJSON 对比及展望。
5050

tutorial01/tutorial01.md

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@
77

88
本单元内容:
99

10-
1. [JSON 是什么](#json-是什么)
11-
2. [搭建编译环境](#搭建编译环境)
12-
3. [头文件与 API 设计](#头文件与-api-设计)
13-
4. [JSON 语法子集](#json-语法子集)
14-
5. [单元测试](#单元测试)
15-
6. [宏的编写技巧](#宏的编写技巧)
16-
7. [实现解析器](#实现解析器)
17-
8. [关于断言](#关于断言)
18-
9. [总结与练习](#总结与练习)
19-
10. [常见问答](#常见问答)
10+
1. [JSON 是什么](#1-json-是什么)
11+
2. [搭建编译环境](#2-搭建编译环境)
12+
3. [头文件与 API 设计](#3-头文件与-api-设计)
13+
4. [JSON 语法子集](#4-json-语法子集)
14+
5. [单元测试](#5-单元测试)
15+
6. [宏的编写技巧](#6-宏的编写技巧)
16+
7. [实现解析器](#7-实现解析器)
17+
8. [关于断言](#8-关于断言)
18+
9. [总结与练习](#9-总结与练习)
19+
10. [常见问答](#10-常见问答)
2020

21-
## JSON 是什么
21+
## 1. JSON 是什么
2222

23-
JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为[ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)
23+
JSON(JavaScript Object Notation)是一个用于数据交换的文本格式,现时的标准为[ECMA-404](https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)
2424

2525
虽然 JSON 源至于 JavaScript 语言,但它只是一种数据格式,可用于任何编程语言。现时具类似功能的格式有 XML、YAML,当中以 JSON 的语法最为简单。
2626

@@ -68,7 +68,7 @@ JSON(JavaScript Object Notation)是一个用于数据交换的文本格式
6868

6969
我们会逐步实现这些需求。在本单元中,我们只实现最简单的 null 和 boolean 解析。
7070

71-
## 搭建编译环境
71+
## 2. 搭建编译环境
7272

7373
我们要做的库是跨平台、跨编译器的,同学可使用任意平台进行练习。
7474

@@ -90,7 +90,7 @@ JSON(JavaScript Object Notation)是一个用于数据交换的文本格式
9090

9191
按 Configure,选择编译器,然后按 Generate 便会生成 Visual Studio 的 .sln 和 .vcproj 等文件。注意这个 build 目录都是生成的文件,可以随时删除,也不用上传至仓库。
9292

93-
在 OS X 下,建议安装 [Homebrew](http://brew.sh/),然后在命令行键入:
93+
在 OS X 下,建议安装 [Homebrew](https://brew.sh/),然后在命令行键入:
9494

9595
~~~
9696
$ brew install cmake
@@ -126,7 +126,7 @@ $ ./leptjson_test
126126

127127
若看到类似以上的结果,说明已成功搭建编译环境,我们可以去看看那几个代码文件的内容了。
128128

129-
## 头文件与 API 设计
129+
## 3. 头文件与 API 设计
130130

131131
C 语言有头文件的概念,需要使用 `#include`去引入头文件中的类型声明和函数声明。但由于头文件也可以 `#include` 其他头文件,为避免重复声明,通常会利用宏加入 include 防范(include guard):
132132

@@ -193,9 +193,9 @@ enum {
193193
lept_type lept_get_type(const lept_value* v);
194194
~~~
195195
196-
## JSON 语法子集
196+
## 4. JSON 语法子集
197197
198-
下面是此单元的 JSON 语法子集,使用 [RFC7159](http://rfc7159.net/rfc7159) 中的 [ABNF](https://tools.ietf.org/html/rfc5234) 表示:
198+
下面是此单元的 JSON 语法子集,使用 [RFC7159](https://tools.ietf.org/html/rfc7159) 中的 [ABNF](https://tools.ietf.org/html/rfc5234) 表示:
199199
200200
~~~
201201
JSON-text = ws value ws
@@ -222,11 +222,11 @@ true = "true"
222222
* 若一个值之后,在空白之后还有其他字符,传回 `LEPT_PARSE_ROOT_NOT_SINGULAR`。
223223
* 若值不是那三种字面值,传回 `LEPT_PARSE_INVALID_VALUE`。
224224
225-
## 单元测试
225+
## 5. 单元测试
226226
227227
许多同学在做练习题时,都是以 `printf`/`cout` 打印结果,再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂,这个做法会越来越低效。一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后,原来的功能维持正确(这称为回归测试/regression testing)。
228228
229-
常用的单元测试框架有 xUnit 系列,如 C++ 的 [Google Test](https://github.com/google/googletest)、C# 的 [NUnit](http://www.nunit.org/)。我们为了简单起见,会编写一个极简单的单元测试方式。
229+
常用的单元测试框架有 xUnit 系列,如 C++ 的 [Google Test](https://github.com/google/googletest)、C# 的 [NUnit](https://www.nunit.org/)。我们为了简单起见,会编写一个极简单的单元测试方式。
230230
231231
一般来说,软件开发是以周期进行的。例如,加入一个功能,再写关于该功能的单元测试。但也有另一种软件开发方法论,称为测试驱动开发(test-driven development, TDD),它的主要循环步骤是:
232232
@@ -243,7 +243,7 @@ TDD 是先写测试,再实现功能。好处是实现只会刚好满足测试
243243
244244
回到 leptjson 项目,`test.c` 包含了一个极简的单元测试框架:
245245
246-
~~~
246+
~~~c
247247
#include <stdio.h>
248248
#include <stdlib.h>
249249
#include <string.h>
@@ -299,7 +299,7 @@ int main() {
299299

300300
然而,完全按照 TDD 的步骤来开发,是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后,先写部分测试代码,再写满足那些测试的实现。
301301

302-
## 宏的编写技巧
302+
## 6. 宏的编写技巧
303303

304304
有些同学可能不了解 `EXPECT_EQ_BASE` 宏的编写技巧,简单说明一下。反斜线代表该行未结束,会串接下一行。而如果宏里有多过一个语句(statement),就需要用 `do { /*...*/ } while(0)` 包裹成单个语句,否则会有如下的问题:
305305

@@ -344,7 +344,7 @@ else
344344
c();
345345
~~~
346346

347-
## 实现解析器
347+
## 7. 实现解析器
348348

349349
有了 API 的设计、单元测试,终于要实现解析器了。
350350

@@ -419,15 +419,15 @@ static int lept_parse_value(lept_context* c, lept_value* v) {
419419

420420
由于 `lept_parse_whitespace()` 是不会出现错误的,返回类型为 `void`。其它的解析函数会返回错误码,传递至顶层。
421421

422-
## 关于断言
422+
## 8. 关于断言
423423

424424
断言(assertion)是 C 语言中常用的防御式编程方式,减少编程错误。最常用的是在函数开始的地方,检测所有参数。有时候也可以在调用函数后,检查上下文是否正确。
425425

426-
C 语言的标准库含有 [`assert()`](http://en.cppreference.com/w/c/error/assert) 这个宏(需 `#include <assert.h>`),提供断言功能。当程序以 release 配置编译时(定义了 `NDEBUG` 宏),`assert()` 不会做检测;而当在 debug 配置时(没定义 `NDEBUG` 宏),则会在运行时检测 `assert(cond)` 中的条件是否为真(非 0),断言失败会直接令程序崩溃。
426+
C 语言的标准库含有 [`assert()`](https://en.cppreference.com/w/c/error/assert) 这个宏(需 `#include <assert.h>`),提供断言功能。当程序以 release 配置编译时(定义了 `NDEBUG` 宏),`assert()` 不会做检测;而当在 debug 配置时(没定义 `NDEBUG` 宏),则会在运行时检测 `assert(cond)` 中的条件是否为真(非 0),断言失败会直接令程序崩溃。
427427

428428
例如上面的 `lept_parse_null()` 开始时,当前字符应该是 `'n'`,所以我们使用一个宏 `EXPECT(c, ch)` 进行断言,并跳到下一字符。
429429

430-
初使用断言的同学,可能会错误地把含副作用的代码放在 `assert()` 中:
430+
初使用断言的同学,可能会错误地把含[副作用](https://en.wikipedia.org/wiki/Side_effect_(computer_science))的代码放在 `assert()` 中:
431431

432432
~~~c
433433
assert(x++ == 0); /* 这是错误的! */
@@ -437,15 +437,15 @@ assert(x++ == 0); /* 这是错误的! */
437437

438438
另一个问题是,初学者可能会难于分辨何时使用断言,何时处理运行时错误(如返回错误值或在 C++ 中抛出异常)。简单的答案是,如果那个错误是由于程序员错误编码所造成的(例如传入不合法的参数),那么应用断言;如果那个错误是程序员无法避免,而是由运行时的环境所造成的,就要处理运行时错误(例如开启文件失败)。
439439

440-
## 总结与练习
440+
## 9. 总结与练习
441441

442442
本文介绍了如何配置一个编程环境,单元测试的重要性,以至于一个 JSON 解析器的子集实现。如果你读到这里,还未动手,建议你快点试一下。以下是本单元的练习,很容易的,但我也会在稍后发出解答篇。
443443

444444
1. 修正关于 `LEPT_PARSE_ROOT_NOT_SINGULAR` 的单元测试,若 json 在一个值之后,空白之后还有其它字符,则要返回 `LEPT_PARSE_ROOT_NOT_SINGULAR`
445445
2. 参考 `test_parse_null()`,加入 `test_parse_true()``test_parse_false()` 单元测试。
446446
3. 参考 `lept_parse_null()` 的实现和调用方,解析 true 和 false 值。
447447

448-
## 常见问答
448+
## 10. 常见问答
449449

450450
1. 为什么把例子命名为 leptjson?
451451

tutorial01_answer/tutorial01_answer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static void test_parse() {
6969
}
7070
~~~
7171

72-
但要记得在上一级的测试函数 `test_parse()` 调用这函数,否则会不起作用。还好如果我们记得用 `static` 修饰这两个函数,编译器会发出告警
72+
但要记得在上一级的测试函数 `test_parse()` 调用这函数,否则会不起作用。还好如果我们记得用 `static` 修饰这两个函数,编译器会发出警告
7373

7474
~~~
7575
test.c:30:13: warning: unused function 'test_parse_true' [-Wunused-function]

tutorial02/leptjson.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;
55

66
typedef struct {
7-
double n;
7+
double n;
88
lept_type type;
99
}lept_value;
1010

0 commit comments

Comments
 (0)