进行数据解析的理由不计其数,相关的工具和技巧也同样如此。但是,当您需要用这些数据做一些新的事情时,即使有“合适的”工具可能也是不够的。这一担心对于异类数据源的集成同样存在。用来做这项工作的合适工具迟早应该是一种编程语言。

Oracle 提供了一些非常强大的实用程序来加载、处理和卸载数据。SQL*Loader、Data Pump、外部表、Oracle Text、正则表达式都能提供这些功能。然而人们常常会需要在数据库外做一些事情(或者,说得琐碎些,可能您还没有获得必要的数据库权限)。

利用 Python 可以进行高水平的、有效的数据解析。而利用互联网上免费提供的大量标准库和众多模块可以处理数据逻辑,不必手动剖析字节。

字符串理论

文本解析的最低级别是字符串。Python 并不把字符区分为单独的数据类型,但却区分普通字符串和 Unicode 字符串类型。字符串可以括在单引号、双引号或三引号中,并且是 Python 的一种不可变对象 — 一旦创建就不能对其进行修改。每一个操作都会创建一个新的字符串对象。对于具有静态类型语言经验的编程人员而言,乍听上去这可能真得很奇怪,但此类实现有一些特定的原因,多数与性能有关。

因为 Python 完全支持 Unicode,所以处理多语言信息不存在问题。手动创建 Unicode 字符串时,您可以选择直接在字符串前使用 u 前缀(如 u"Unicode text")或者使用内置的 unicode() 函数。可以使用 unicode() 或 encode() 方法在任何支持的字符集中对字符串进行编码。有关支持的编码列表,请查阅 Python 库参考 的标准编码部分或使用导入编码;输出 encodings._aliases.keys()。

您可以放心地使用 UTF-8 编写 Python 程序,记住仅变量名必须是有效的 ASCII 字符串。注释可以是希腊文、汉字或任意内容。不过,这样的文件或者要求使用附加字节顺序标记 (BOM) 的编辑器来保存,或者,需要您编写第一行代码:

# -*- coding: utf-8 -*-

字符串提供有一组方法可用于进行大多数有用的文本操作,如 find()、split()、rjust() 或 upper()。它们在内置的 str 类型上实现,该类型可以表示普通字符串和原始字符串。(原始字符串与普通字符串对反斜线的解释不同。)

>>>  zen = "Explicit is better than implicit."
>>> print zen.title()
'Explicit Is Better Than Implicit.'
>>> zen.split(' ')
['Explicit', 'is', 'better', 'than', 'implicit.']
>>> zen.rstrip('.').lower().replace('is', 'is always')
'explicit is always better than implicit'

Python 的可迭代类型的最棒的一个特性是索引方法。普通索引以 0 开始而负索引向后计数,所以 [-1] 表示最后一个字符,[:5] 表示前 5 个字符,而 [5:-5] 表示前 5 个和后 5 个字符组成的字符串。

>>>  sample = "Oracle Database"
>>> sample[0]
'O'
>>> sample[0:6], sample[7:15]
('Oracle', 'Database')
>>> sample[-8:]
'Database'
>>> sample[sample.index('Data')+4:]
'base'

正则表达式

Python 当然支持正则表达式。事实上,Python 的正则表达式 re 模块支持 Unicode、匹配、搜索、拆分、替换和分组。如果您熟悉 Oracle 对正则表达式的实现方式,您就不会对 Python 的函数感到陌生。

在详细比较 Python 和 Oracle 对正则表达式的实现时,值得注意的差异包括:

  • 当关系设计要求一种不同于编程语言 1 的方法时,re.search() 可用于代替 Oracle 的 REGEXP_LIKE、REGEXP_INSTR 和 REGEXP_SUBSTR。

  • 对 Python 语法改写后,re.sub() 的使用方式可以与 REGEXP_REPLACE 完全相同。不过,要注意 Oracle 的位置参数从 1 开始,而 Python 编制任何索引时都从 0 开始。

  • Oracle 的 match_parameter 表示正则表达式的一组标志,方式与 Python 在搜索模式或模式对象编译属性中使用 (?iLmsux) 语法的方式相同。要获得有效标志的列表,请比较 Python 库参考 的 4.2.3 节和 Oracle 数据库 SQL 语言参考 中 match_parameter 的有效值列表。

Python 的 re.search() 函数非常灵活,这归功于正则表达式这一基本概念。re 模块的最底层有一个对象,它表示匹配模式的方式允许以多种不同的方法对源字符串进行剖析。re.compile() 函数返回一个采用某一模式和若干可选标志的编译模式对象,如 re.I,它表示不区分大小写的匹配。

>>>  import re
>>> p = re.compile("^a.*", re.I)
>>> print p
<_sre.SRE_Pattern object at 0x011CA660>

您无须显式编译正则表达式。re 模块中的函数以透明方式完成此工作。如果代码中多处用到编译模式,使用该模式非常有益,但是如果该模式仅使用一次,则不需要这样的编码开销。

Python 中有六个正则表达式编译标志:

  • I (IGNORECASE) 用于不区分大小写的匹配

  • L (LOCALE) 使得特殊的序列(如词和空格)与语言设置相关

  • M (MULTILINE) 意味着在多行中搜索该模式,这样 ˆ 可以匹配字符串的开始位置和每一个换行符后面的位置,$ 可以匹配每一个换行符前面的位置和字符串的结束位置

  • S (DOTALL) 强制使用点专用字符 (.) 匹配任意字符,包括换行符

  • U (UNICODE) 使得特殊的序列可以识别 Unicode

  • X (VERBOSE) 可以增强您编写的正则表达式的可读性。

要一次使用多个标志,只需将它们加在一起即可 — 如 re.compile("Oracle", re.I+re.S+re.M)。另一种方式是使用 (?iLmsux) 语法将使用所需数量的标志选项作为搜索模式的前缀。这样,前一表达式可写作 re.compile("(?ism)Oracle")。

有关使用正则表达式的最好建议是尽可能地避免使用它们。在将它们嵌入代码前,请确定没有字符串方法可以完成相同的工作,因为字符串方法更快且不会带来导入以及正则表达式处理这些额外的开销。在字符串对象上使用 dir() 就可以看到可用的内容。

下例展示了在 Python 这样一种动态语言中看待正则表达式的方式。解析 tnsnames.ora 文件以便为每个网络别名创建简单连接字符串(将 file() 函数指向您的 tnsnames.ora 文件的位置):

>>> import re
>>> tnsnames = file(r'tnsnames.ora').read()
>>> easy_connects = {}
>>> tns_re = "^(\w+?)\s?=.*?HOST\s?=\s?(.+?)\).*?PORT\s?=\s?(\d+?)\).

*?SERVICE_NAME\s?=\s?(.+?)\)"
>>> for match in re.finditer(tns_re, tnsnames, re.M+re.S):
...  t = match.groups()
...  easy_connects[t[0]] = "%s:%s/%s" % t[1:]
>>> print easy_connects

此程序在 Oracle Database XE 默认的 tnsnames.ora 文件上的输出是:

{'XE': 'localhost:1521/XE'}

请注意,此正则表达式非常愚钝,会被 IPC 条目所阻塞,因此需要将这些条目放在文件的结尾处。解析匹配圆括号是一个 NP 完成问题。

因为提供有多种公开方法,Python 匹配对象的功能非常强大,这些方法包括 span()(它可以返回匹配范围)、group()(它可以按给定的索引返回匹配组)以及 groupdict()(它可以在模式含有命名的组时以字典形式返回匹配组)。

逗号分隔值

CSV 格式因其简洁性和跨平台设计常用于组织间的信息交换。使用正则表达式通常可以轻松地解析逗号分隔值,但使用 Python 的 csv 模块可以使此任务变得更为容易。

使用该模块要求开发人员熟悉该模块所采用的逻辑。有关 CSV 文件的最重要的信息是它的“方言”,它包含分隔符、引号字符、行终止符等相关信息。Python 2.5 中目前可用的方言是 excel 和 excel-tab。内置的嗅探器总是试图猜测正确的格式。写入器与阅读器对象支持 CSV 数据的输入和输出。

就本例而言,我用的是 HR 模式的 JOBS_HISTORY 表中的数据。它演示了如何直接从一个 SQL 查询创建 CSV 文件 job_history.csv。

>>> import csv
>>> import cx_Oracle
>>> db = cx_Oracle.connect('hr/hrpwd@localhost:1521/XE')
>>> cursor = db.cursor()
>>> f = open("job_history.csv", "w")
>>> writer = csv.writer(f, lineterminator="\n", quoting=csv.QUOTE_NONNUMERIC)
>>> r = cursor.execute(" "SELECT * FROM job_history ORDER BY employee_id, start_date")
>>> for row in cursor:
...   writer.writerow(row)
...
>>> f.close()

该文件包含:

101,"1989-09-21 00:00:00","1993-10-27 00:00:00","AC_ACCOUNT",110
101,"1993-10-28 00:00:00","1997-03-15 00:00:00","AC_MGR",110
102,"1993-01-13 00:00:00","1998-07-24 00:00:00","IT_PROG",60
114,"1998-03-24 00:00:00","1999-12-31 00:00:00","ST_CLERK",50
122,"1999-01-01 00:00:00","1999-12-31 00:00:00","ST_CLERK",50
176,"1998-03-24 00:00:00","1998-12-31 00:00:00","SA_REP",80
176,"1999-01-01 00:00:00","1999-12-31 00:00:00","SA_MAN",80
200,"1987-09-17 00:00:00","1993-06-17 00:00:00","AD_ASST",90
200,"1994-07-01 00:00:00","1998-12-31 00:00:00","AC_ACCOUNT",90
201,"1996-02-17 00:00:00","1999-12-19 00:00:00","MK_REP",20

或者,您也可以使用 Oracle SQL Developer 以 CSV 格式导出数据。

可以通过以下方式读取该 CSV 文件:

>>>  reader = csv.reader(open("job_history.csv", "r"))
>>>  for employee_id, start_date, end_date, job_id, department_id in reader:
...   print job_id,
...
JOB_ID IT_PROG AC_ACCOUNT AC_MGR MK_REP ST_CLERK ST_CLERK

AD_ASST SA_REP SA_MAN AC_ACCOUNT

注意我不必在上面显式指定方言,它是自动推断出的。我只是输出了 job_id 列,但对这样经过解析的文件我确实可以做的是将其插入数据库中。为确保日期得到正确处理,在批量插入前对 NLS_DATE_FORMAT 进行手动设置。

SQL> CREATE TABLE job_his (
2 employee_id NUMBER(6) NOT NULL,
3 start_date DATE NOT NULL,
4 end_date DATE NOT NULL,
5 job_id VARCHAR2(10) NOT NULL,
6 department_id NUMBER(4)
7 ); >>> reader = csv.reader(open("job_history.csv", "r"))
>>> lines = []
>>> for line in reader:
... lines.append(line)
...
>>> cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'")
>>> cursor.executemany("INSERT INTO job_his VALUES(:1,:2,:3,:4,:5)", lines)
>>> db.commit()

如果您使用 SQL Developer 创建该 CSV 文件,则可能需要修改日期格式,如下所示:

>>> cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YY/MM/DD'")

csv 模块美中不足的地方是缺乏原生 Unicode 支持。有关解决方案和使用 CSV 文件的更多示例,请参见 Python 库参考 的 9.1.5 示例部分

URL

urlparse 模块使您可以将统一资源定位器字符串分解为各个组成部分,分别表示 URL 模式、网络位置、路径、参数、查询字符串、碎片标识符、用户名、口令、主机名和/或端口。Python 2.5 支持 24 个最常用的模式,包括 svn+ssh、sftp 和 mms。下例显示了 urlparse 模块的一些特性:

>>>  from urlparse import urlparse
>>> url = "http://www.oracle.com/technology/index.html?rssid=rss_otn_news#section5"
>>> pr = urlparse(url)
>>> print type(pr)
<class 'urlparse.ParseResult'>
>>> print pr.hostname
www.oracle.com
>>> print pr.query
rssid=rss_otn_news
>>> print url==pr.geturl()
True

RSS 信源

RSS 基于一个非常简单的概念:您在事件发生时获得它的最新消息,而不是碰巧发现。整合许多不同来源的 RSS 信源是开发领域的一个流行趋势,对新闻信源聚合器和 Web 2.0 混搭尤其如此。

RSS 是 XML 的一种方言,因此使用 Python 提供的一种 XML 解析器可轻松地对其进行处理。Python 标准库本身还没有提供用于解析信源的模块。不过,feedparser.org 免费提供一个稳定的、经过广泛检验的通用信源解析器。由于它没有外部依赖性,因此这是快速熟悉模块安装概念的一个很好的机会。

下载 feedparser 模块的最新版本(撰写此文时为 4.1)后,对其进行解压缩并将工作目录修改为 feedparser-4.1。在控制台/命令提示符下,运行 python setup.py install。此命令将模块放入 Python 文件夹,使其立即可供使用。就是这样。

了解一下 Oracle 的动态如何?

>>> import feedparser
>>> import time
>>> rss_oracle = feedparser.parse("http://www.oracle.com/technology/syndication/rss_otn_news.xml")
>>> for e in rss_oracle.entries[:5]:
..    t = time.strftime("%Y/%m/%d", e.updated_parsed)
..    print t, e.title
        
2007/07/23 Integrating Oracle Spatial with Google Earth
2007/07/11 Oracle Database 11g Technical Product Information Now Available
2007/07/11 Explore the Oracle Secure Enterprise Search Training Center
2007/07/09 Implementing Row-Level Security in Java Applications
2007/06/29 Build Your Own Oracle RAC Cluster on Oracle Enterprise Linux and iSCSI

feedparser 模块具有足够的智能,可以正确解析日期、处理 HTML 标记、规范内容以便可以针对所有支持的 RSS 和 ATOM 变体使用一致的 API、解析相对链接、检测有效字符编码等。

接下来解析什么?

有了正则表达式工具箱,您可以搜索几乎所有的纯文本内容。至于解析文本数据,Python 有很多其他特性,包括:

  • email.parse,用于解析电子邮件消息
  • ConfigParser,用于解析从 Windows 系统中获得的 INI 配置文件
  • robotparser 模块,用于解析您 Web 站点的 robots.txt
  • optparse 模块,用于进行强大的命令行参数解析
  • HTMLParse 模块中的 HTMLParse 类,用于有效地解析 HTML 和 XHTML(类似于 SAX)
  • 若干 XML 解析器(xml.dom、xml.sax、xml.parsers.expat、xml.etree.ElementTree)

对于二进制数据,您可以利用 binascii 模块,它包含一组函数可用于在二进制编码数据和 ASCII 编码数据之间转换,并附带了分别用于 base64 和 uuencode 转换的 base64 和 uu 模块。

总结

这篇方法文档介绍了在 Python 中进行数据解析所采用的一些基本和高级的技巧。您现在应当已经认识到了 Python 附带的标准库的威力。在开始制作您自己的解析器之前,首先检查一下所需的功能是否已可供导入。

字符串操作比正则表达式操作速度快,同时足以满足很多的编程需要。但是到底选用 Python 还是 Oracle 正则表达式函数取决于您的应用程序逻辑和业务需要。

精通 Oracle+Python,第 3 部分:数据解析的更多相关文章

  1. python爬虫的页面数据解析和提取/xpath/bs4/jsonpath/正则(1)

    一.数据类型及解析方式 一般来讲对我们而言,需要抓取的是某个网站或者某个应用的内容,提取有用的价值.内容一般分为两部分,非结构化的数据 和 结构化的数据. 非结构化数据:先有数据,再有结构, 结构化数 ...

  2. python爬虫---爬虫的数据解析的流程和解析数据的几种方式

    python爬虫---爬虫的数据解析的流程和解析数据的几种方式 一丶爬虫数据解析 概念:将一整张页面中的局部数据进行提取/解析 作用:用来实现聚焦爬虫的吧 实现方式: 正则 (针对字符串) bs4 x ...

  3. 精通 Oracle+Python,第 9 部分:Jython 和 IronPython — 在 Python 中使用 JDBC 和 ODP.NET

    成功的编程语言总是会成为顶级开发平台.对于 Python 和世界上的两个顶级编程环境 Java 和 Microsoft .NET 来说的确如此. 虽然人们因为 Python 能够快速组装不同的软件组件 ...

  4. 精通 Oracle+Python,第 2 部分:处理时间和日期

    从 Python 2.4 版开始,cx_Oracle 自身可以处理 DATE 和 TIMESTAMP 数据类型,将这些列的值映射到 Python 的 datetime 模块的 datetime 对象中 ...

  5. Python爬虫之三种数据解析方式

    一.引入 二.回顾requests实现数据爬取的流程 指定url 基于requests模块发起请求 获取响应对象中的数据 进行持久化存储 其实,在上述流程中还需要较为重要的一步,就是在持久化存储之前需 ...

  6. 05 Python网络爬虫的数据解析方式

    一.爬虫数据解析的流程 1.指定url 2.基于requests模块发起请求 3.获取响应中的数据 4.数据解析 5.进行持久化存储 二.解析方法 (1)正则解析 (2)bs4解析 (3)xpath解 ...

  7. 精通 Oracle+Python,第 1 部分:查询最佳应践

    原文链接:http://www.oracle.com/technetwork/cn/articles/dsl/mastering-oracle-python-1391323-zhs.html 在 Py ...

  8. 精通 Oracle+Python,第 8 部分:适合 Oracle DBA 使用的 Python

    传统上,当需要为操作系统编写一些脚本时,人们常常会选用 Bash 或 Perl 脚本工具.这些工具易于使用,因而它们几乎变得无处不在,渗透到了包括 Oracle Database 在内的其他软件中,O ...

  9. 精通 Oracle+Python,第 6 部分:Python 支持 XML

    无可辩驳的是,XML 现在是软件中信息交换的实际标准. 因此,Oracle 数据库附带了各种与 XML 相关的增强和工具,它们统称为 Oracle XML DB.XML DB 包含一系列嵌入到数据库中 ...

随机推荐

  1. Coding girl一个老程序员谈到的一个女程序员的故事

    因为有人说我给一个女程序员的建议不靠谱,我不服,因为我的工作经历中的一些女程序员都很不错,比那些男程序员都强,所以,我在新浪微博和twitter上征集女程序员的故事和想法,这两天来,我收到了好几封邮件 ...

  2. action使用大全

    1.Intent的用法: (1)Action跳转 1. 使用Action跳转,当程序AndroidManifest.xml中某一个 Activity的IntentFilter定义了包含Action, ...

  3. android82 启动Actvity和Activity的生命周期

    package com.itheima.jump; import android.net.Uri; import android.os.Bundle; import android.app.Activ ...

  4. 刷leetcode

    目前已经刷到了56题,才到1/3,后面越来越难,不知道能不能在面微软之前刷完.

  5. Chapter 4 - How to Fire some Bullets

    Now, we want to let the hero fire some bullets to kill the enemies, add the codes below to set the l ...

  6. android开发之gridlayout使用入门

    这个东东以前没怎么用过,今天研究了一下,不难,感觉只是在某些方面很好用. 记录下,做个计算器. <GridLayout xmlns:android="http://schemas.an ...

  7. Ⅴ.AngularJS的点点滴滴-- 资源和过滤

    资源ngResource(依赖ngResource模块) <html> <script src="http://ajax.googleapis.com/ajax/libs/ ...

  8. ListView simpleAdapter的基本使用

    使用simpleAdapter的数据用一般都是HashMap构成的List,list的每一节对应ListView的每一行.HashMap的每个键 值数据映射到布局文件中对应id的组件上.因为系统没有对 ...

  9. Mysql中int(1)的误解及说明

    在mysql中使用int相关的数据类型时, 如果不太了解其存储方式, 会产生一些误用的情况. 如: 只保存0-9之间的数字, 可能会直接用int(1). 习惯性的以为int(1)就相当于varchar ...

  10. 用标准版的Eclipse搭建PHP环境

    用标准版的Eclipse搭建PHP环境 ——@梁WP 摘要:用标准版的Eclipse搭建PHP环境. 一.下载Eclipse的PHP插件 百度搜索phpeclipse,看到某条结果是带有SourceF ...