Python實現JSON產生器和遞迴下降解譯器

來源:互聯網
上載者:User

標籤:publish   目標   ddn   空白   提高   2.7   div   直接   eric   

Python實現JSON產生器和遞迴下降解譯器

github地址:https://github.com/EStormLynn/Python-JSON-Parser

目標

從零開始寫一個JSON的解析器,特徵如下:

  • 符合標準的JSON解析器和產生器
  • 手寫遞迴下降的解譯器(recursive descent parser)
  • 使用Python語言(2.7)
  • 解譯器和產生器少於500行
  • 使用cProfile完成效能分析和最佳化
實現內容
  • [x] 解析字面量(true false null)
  • [x] 解析數字
  • [x] 解析字串
  • [x] 解析Unicode
  • [x] 解析數組
  • [x] 解析對象
  • [x] 單元測試
  • [x] 產生器
  • [x] cProfile效能最佳化
詳細介紹JSON是什麼

JSON(JavaScript Object Notation)是一個用於資料交換的文字格式設定,參考ecma標準,JSON Data Interchange Format,先看一段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的樹狀結構中

  • null: 表示為 null
  • boolean: 表示為 true 或 false
  • number: 一般的浮點數表示方式,在下一單元詳細說明
  • string: 表示為 "..."
  • array: 表示為 [ ... ]
  • object: 表示為 { ... }
實現解譯器

es_parser 是一個手寫的遞迴下降解析器(recursive descent parser)。由於 JSON 文法特別簡單,可以將分詞器(tokenizer)省略,直接檢測下一個字元,便可以知道它是哪種類型的值,然後調用相關的分析函數。對於完整的 JSON 文法,跳過空白後,只需檢測當前字元:

n ? literalt ? truef ? false" ? string0-9/- ? number[ ? array{ ? object

對於json的typevalue和json string編寫了這樣2個類

class EsValue(object):    __slots__ = (‘type‘, ‘num‘, ‘str‘, ‘array‘, ‘obj‘)        def __init__(self):        self.type = JTYPE_UNKNOWclass context(object):    def __init__(self, jstr):        self.json = list(jstr)        self.pos = 0

以解析多餘的空格,製表位,換行為例:

def es_parse_whitespace(context):    if not context.json:        return    pos = 0    while re.compile(‘[\s]+‘).match(context.json[pos]):        pos += 1    context.json = context.json[pos:]
解析字面量

字面量包括了false,true,null三種。

def es_parse_literal(context, literal, mytype):    e_value = EsValue()    if ‘‘.join(context.json[context.pos:context.pos + len(literal)]) != literal:        raise MyException("PARSE_STATE_INVALID_VALUE, literal error")    e_value.type = mytype    context.json = context.json[context.pos + len(literal):]    return PARSE_STATE_OK, e_valuedef es_parse_value(context, typevalue):    if context.json[context.pos] == ‘t‘:        return es_parse_literal(context, "true", JTYPE_TRUE)    if context.json[context.pos] == ‘f‘:        return es_parse_literal(context, "false", JTYPE_FALSE)    if context.json[context.pos] == ‘n‘:        return es_parse_literal(context, "null", JTYPE_NULL)
解析數字

JSON number類型,number 是以十進位表示,它主要由 4 部分順序組成:負號、整數、小數、指數。只有整數是必需部分。

JSON 可使用科學記號標記法,指數部分由大寫 E 或小寫 e 開始,然後可有加號或減號,之後是一或多個數字(0-9)。

JSON 標準 ECMA-404 採用圖的形式表示文法,可以更直觀地看到解析時可能經過的路徑:

python是一種動態語言,所以es_value中num可以是整數也可以是小數,

class es_value():    def __init__(self, type):        self.type = type        self.num = 0

python對於string類型,可以強制轉換成float和int,但是int(string)無法處理科學記號標記法的情況,所以統一先轉成float在轉成int

typevalue.num = float(numstr)if isint:    typevalue.num = int(typevalue.num)

實現的單元測試包含:

    def testnum(self):        print("\n------------test number-----------")        self.assertEqual(type(self.parse("24")), type(1))        self.assertEqual(type(self.parse("1e4")), type(10000))        self.assertEqual(type(self.parse("-1.5")), type(-1.5))        self.assertEqual(type(self.parse("1.5e3")), type(1.500))
解析字串

對於字串中存在逸出字元,在load的時候須要處理逸出字元,\u的情況,進行編碼成unicode

def es_parse_string(context):    charlist = {        ‘\\"‘: ‘\"‘,        "\\‘": "\‘",        "\\b": "\b",        "\\f": "\f",        "\\r": "\r",        "\\n": "\n",        "\\t": "\t",        "\\u": "u",        "\\\\": "\\",        "\\/": "/",        "\\a": "\a",        "\\v": "\v"    }    while context.json[pos] != ‘"‘:        # 處理轉意字元        if context.json[pos] == ‘\\‘:            c = context.json[pos:pos + 2]            if c in charlist:                e_value.str += charlist[c]            else:                e_value.str += ‘‘.join(context.json[pos])                pos += 1                continue            pos += 2        else:            e_value.str += ‘‘.join(context.json[pos])            pos += 1        e_value.type = JTYPE_STRING        context.json = context.json[pos + 1:]        context.pos = 1        if ‘\u‘ in e_value.str:            e_value.str = e_value.str.encode(‘latin-1‘).decode(‘unicode_escape‘)        return PARSE_STATE_OK, e_value

單元測試:

    def teststring(self):        print("\n------------test string----------")        self.assertEqual(type(self.parse("\" \\\\line1\\nline2 \"")), type("string"))         # input \\  is \        self.assertEqual(type(self.parse("\"  abc\\def\"")), type("string"))        self.assertEqual(type(self.parse("\"      null\"")), type("string"))        self.assertEqual(type(self.parse("\"hello world!\"")), type("string"))        self.assertEqual(type(self.parse("\"   \u751F\u5316\u5371\u673A  \"")), type("string"))
es_dumps函數,json產生器

將python dict結構dumps成json串

def es_dumps(obj):    obj_str = ""    if isinstance(obj, bool):        if obj is True:            obj_str += "True"        else:            obj_str += "False"    elif obj is None:        obj_str += "null"    elif isinstance(obj, basestring):        for ch in obj.decode(‘utf-8‘):            if u‘\u4e00‘ <= ch <= u‘\u9fff‘:                obj_str += "\"" + repr(obj.decode(‘UTF-8‘)) + "\""                break        else:            obj_str += "\"" + obj + "\""    elif isinstance(obj, list):        obj_str += ‘[‘        if len(obj):            for i in obj:                obj_str += es_dumps(i) + ", "            obj_str = obj_str[:-2]        obj_str += ‘]‘    elif isinstance(obj, int) or isinstance(obj, float):     # number        obj_str += str(obj)    elif isinstance(obj, dict):        obj_str += ‘{‘        if len(obj):            for (k, v) in obj.items():                obj_str += es_dumps(k) + ": "                obj_str += es_dumps(v) + ", "            obj_str = obj_str[:-2]        obj_str += ‘}‘    return obj_str
cProfile效能分析

匯入cProfile模組進行效能分析,load中國34個省份地區人口發布,

import cProfilefrom jsonparser import *import jsoncProfile.run("print(es_load(\"china.json\"))")

修改部分代碼使用python build-in,最佳化context結構,string在copy的時候比list效能顯著提高。消耗時間從20s降到1s

Python實現JSON產生器和遞迴下降解譯器

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.