数据编码与处理

 

一、读写CSV数据

  (1)使用csv库处理CSV数据

import csv
with open('./stock.csv') as f:
    f_csv = csv.reader(f)
    headers = next(f_csv)
    for row in f_csv:
        # process row

  由于每一行的row是个列表,访问需要用row[0]、row[1],

  (2)可以考虑转换成命名元组访问。

import csv
from collections import namedtuple

with open('./stock.csv') as f:
    f_csv = csv.reader(f)
    headers = next(f_csv)
    Row = namedtuple('Row',headers)
    for r in f_csv:
        row = Row(*r)
        # process row

  (3)转换为字典

import csv
with open('./stock.csv') as f:
    f_csv = csv.DictReader(f)
    for row in f_csv:
        # process row

  写入CSV数据:

import csv
headers = ['Symbol', 'Price', 'Date', 'Time', 'Change', 'Volume']
rows = [
    ('AA', '39.48', '6/11/2007', '9:34am', '-0.18', '428900'),
    ('BB', '48.54', '8/25/2001', '19:57am', '-0.44', '142800'),
    ('CC', '92.13', '3/18/1886', '3:11am', '-0.67', '126700'),
    ('DD', '79.25', '2/05/1999', '8:22am', '-0.27', '110000'),
]

with open('stock2.csv','w') as f:
    f_csv = csv.writer(f)
    f_csv.writerow(headers)
    f_csv.writerows(rows)

  如果数据是字典序列,那么可以这样处理:

import csv
headers = ['Symbol', 'Price', 'Date', 'Time', 'Change', 'Volume']
rows = [
    {'Symbol':'AA','Price':39.48,'Date':'6/11/2007', 'Time':'9:34am', 'Change':-0.18, 'Volume':428900}
]

with open('stock2.csv','w') as f:
    f_csv = csv.DictWriter(f, headers)
    f_csv.writeheader()
    f_csv.writerows(rows)

  标题行出现非法字符,需要进行转换。

import re
with open('./stock.csv') as f:
    f_csv = csv.reader(f)
    headers = [ re.sub('[^a-zA-Z_]', '_', h) for h in next(f_csv)]

  读取数据时,将部分数据转换成除字符串之外的类型。

import csv,re

col_type = [str,float,str,str,float,str]
with open('./stock.csv') as f:
    f_csv = csv.reader(f)
    headers = [ re.sub('[^a-zA-Z_]', '_', h) for h in next(f_csv)]
for row in f_csv: row = tuple( convert(value)for convert, value in zip(col_type, row) )

  字段转化成字典:

field_type = [
    ('Price',float),
    ('Change',float),
    ('Volume',int),
]

with open('./stock.csv') as f:
    for row in csv.DictReader(f):
        row.update( (key,convert(row[key])) for key, convert in field_type)
        print(row)

 

二、读写JSON数据

  (1)字符串形式:json.dumps()、json.loads()

  (2)文件形式:json.dump()、json.load()

  (3)使用pprint()函数,合理格式输出 或者 在json.dumps()函数中使用indext参数

  >>> from urllib.request import urlopen

  >>> pprint(json_resp)

  >>> print(json.dumps(data, indent=4))

  (4)load时解码为OrderDict有序字典

  >>> from collections import OrderedDict

  >>> data = json.loads(s, object_pairs_hook=OrderedDict)

  (5)JSON字典转变为Python对象

class JSONObject:
    def __init__(self,d):
        self.__dict__ = d

>>> data = json.loads(s, object_hook=JSONObject)
>>> data.name

  (6)序列化类实例,提供一个函数作为输入并返回一个可以被序列化处理的字典。

def serialize_instance(obj):
    obj = { '__classname__' : type(obj).__name__ }
    obj.update(vars(obj))
    return obj

class Point:
    def __init__(self,x,y):
        self.x = x
        self.y = y

classes = {
    'Point':Point
}

def unserialize_instance(d):

    clsname = d.pop('__classname__',None)
    if clsname:
        cls = classes[clsname]
        obj = cls.__new__(cls)
        for key,value in d.items():
            setattr(obj,key,value)
            return obj
    return d

>>> p = Point(2, 3)
>>> s = json.dumps(p, default  = serialize_instance)
>>> a = json.loads(s, object_hook = unserialize_instance )

 

三、解析简单的XML文档

  xml.etree.ElementTree.parse()函数将整个XML文档解析为一个文档对象。

  之后,就可以利用find()、iterfind()、findtext()方法查询特定的XML元素。

  (1)指定标签时,需要整体考虑文档的结构。每一个查找操作都是相对于一个起始元素来展开的。

  (2)doc.iterfind('channel/item')的调用会查找所有在channel元素之下的item元素。doc代表着文档的顶层。

  (3)之后对item.findtext()的调用就相对于已找到的item元素来展开。

  (4)每个由ElementTree模块所表示的单个元素都有重要的属性和方法,tag属性包含标签的名称,text属性包含附着的文本,get()方法可以用来提取出属性。

 

四、以增量的方式解析大型XML文件

  从大型XML文档中提取出数据时,使用迭起器和生成器。

<?xml version="1.0" encoding="ISO-8859-1"?>

<!-- Edited with XML Spy v2007 (http://www.altova.com) -->

<breakfast_menu>
<food>
    <food>

        <name>Belgian Waffles</name>

        <price>$5.95</price>

        <description>two of our famous Belgian Waffles with plenty of real maple syrup</description>

        <calories>650</calories>

    </food>

    <food>

        <name>Strawberry Belgian Waffles</name>

        <price>$7.95</price>

        <description>light Belgian waffles covered with strawberries and whipped cream</description>

        <calories>900</calories>

    </food>

    <food>

        <name>Berry-Berry Belgian Waffles</name>

        <price>$8.95</price>

        <description>light Belgian waffles covered with an assortment of fresh berries and whipped cream</description>

        <calories>900</calories>

    </food>

    <food>

        <name>French Toast</name>

        <price>$4.50</price>

        <description>thick slices made from our homemade sourdough bread</description>

        <calories>600</calories>

    </food>

    <food>

        <name>Homestyle Breakfast</name>

        <price>$6.95</price>

        <description>two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>

        <calories>950</calories>

    </food>
</food>
</breakfast_menu>
simple.xml

  iterparse()方法允许我们对XML文档做增量式处理;

  迭代器产生出形式为(event, elem)的元组,event是列出的事件,而elem是对应的XML元素。

from xml.etree.ElementTree import iterparse

def parse_and_remove(filename, path):

    tag_stack = []
    elem_stack = []
    path_parts = path.split('/')

    doc = iterparse(filename, ('start', 'end'))
    next(doc)
    for event, elem in doc:
        if event == 'start':
            tag_stack.append(elem.tag)
            elem_stack.append(elem)
        elif event == 'end':
            if tag_stack == path_parts:
                yield elem
                elem_stack[-2].remove(elem)
            try:
                tag_stack.pop()
                elem_stack.pop()
            except IndexError as e:
                pass

data = parse_and_remove('./simple.xml','food/food')
for i in data:
    print('>>>>>>>>>>>>>>>>',i,'<<<<<<<<<<<<<<<')

  elem_stack[-2].remove(elme),这一行代码使得之前通过yield产生的元素从它们的父节点中移除。

  因此可假设其再也没有任何其他的引用存在,因此该元素被销毁进而可以回收它所占用的内存。

 

五、将字典转换为XML

  xml.etree.ElementTree库同样可以用来创建XML文档。

from xml.etree.ElementTree import Element
def dict_to_xml(tag, d):
    elem = Element(tag)
    for key, val in d.items():
        child = Element(key)
        child.text = str(val)
        elem.append(child)
    return elem

>>> s = { 'name':'GGGG','share':'100','price':'23.44'}
>>> e = dict_to_xml('stock', s)
<Element 'stock' at 0x000001CAFEE80F98>

  转换的结果是得到一个Element实例。可以利用xml.etree.ElementTree库中的tostring()函数将其转换为字节串。

>>> from xml.etree.ElementTree import tostring
>>> tostring(e)
b'<stock><name>GGGG</name><price>23.44</price><share>100</share></stock>'

  为元素附加属性,使用set()方式实现。

  如果要考虑元素间的顺序,创建OrderedDict有序字典来取代普通的字典。

  当创建XML时,倾向于只使用字符串来完成:

def str_to_xml(tag, d):
    parts = ['<{}>'.format(tag)]
    for key, val in d.items():
        parts.append('<{0}>{1}</{0}>'.format(key, val))
    parts.append('</{}>'.format(tag))
    return ''.join(parts)

>>> e = str_to_xml('stock', s)
<stock><name>GGGG</name><price>23.44</price><share>100</share></stock>

  当字典中包含有特殊字符时:{ 'name' : '<spam>' }

>>> d = {'name':'<spam>'}
>>> tostring(dict_to_xml('stock',d))
b'<stock><name>&lt;spam&gt;</name></stock>'
>>> str_to_xml('stock',d)
<stock><name><spam></name></stock>

  需要手工对字符做转义处理,可以使用xml.sax.saxutils中的escape()和unescape()函数。

>>> from xml.sax.saxutils import escape,unescape
>>> escape('<spam>')
'&lt;spam&gt;'
>>> unescape(_)
'<spam>'

 

六、解析、修改和重写XML

  修改XML文档的结构主要是对父元素进行操作,如果需要移除某个元素,那么就利用它的直接父节点的remove()方法完成。

  如果插入或添加新的元素,使用父节点的insert()和append()方法来完成。这些元素也可以使用索引和切片操作来进行操控,比如element[i]或element[i:j]。

  >>> from xml.etree.ElementTree impor parse,Element

  >>> doc = parse('simple.xml')

  >>> root = doc.getroot()

  >>> root.remove(root.find('sri'))

  >>> root.getchildren().index(root.find('nm'))  # 1

  >>> e = Element('spam')

  >>> e.text = 'This is a test'

  >>> root.insert(2, e)  # 插入在<nm>...</nm> 后面 ..

  >>> doc.write('new_simple.xml', xml_declaration=True)

 

七、用命名空间来解析XML文档

  对包含有命名空间的XML文档进行解析会非常繁琐。XMLNamespaces类的功能只是用来稍微简化一下这个过程,

  能够在后续的操作中使用缩短的命名空间名称,而不必去使用完全限定的URI。

class XMLNamespace:
    
    def __init__(self, **kwargs):
        self.namespace = {}
        for name, uri in kwargs.items():
            self.register(name, uri)
            
    def register(self,name, uri):
        self.namespace[name] = '{'+uri+'}'
    
    def __call__(self, path):
        return path.format_map(self.namespace)

>>> ns = XMLNamespace(html='http://www.w3.org/1999/xhtml')
>>> doc.find(ns('content/{html}/html/{html}/head/{html}/title'))
'Hello World'

  正在解析的文本除了命名空间之外的其他高级XML特性,那么最好还是使用lxml库。

 

九、编码和解码十六进制数字

  (1)字节流 =》十六进制数组成的字符串

  >>> import binascii

  >>> s = b'hello'

  >>> h = binascii.b2a_hex(s)  b'68656c6c6f'

  (2)十六进制数组成的字符串 =》字节流

  >>> binascii.a2b_hex(h)  b'hello'

  (3)base64模块下,字节流 =》十六进制数组成的字符串

  >>> import base64

  >>> h = base64.bl6encode(s)  b'68656C6C6F'

  (4)base64模块下,十六进制数组成的字符串 =》字节流

  >>> s = base64.bl6decode(h)  b'hello'

  base64.bl64encode和base64.bl64decode只能对大写形式的十六进制数进行操作,而binascii模块能够处理任意一种情况。

  

十、Base64编码和解码

  采用Base64编码对二进制数据做编码解码操作。

  (1)字节串编码

  >>> import base64

  >>> s = b'hello'

  >>> a = base64.b64encode(s)  b'aGVsbG8'

  (2)解码

  >>> base64.n64decode(a)  b'hello'

 

posted @ 2019-09-13 00:01  5_FireFly  阅读(429)  评论(0编辑  收藏  举报
web
counter