用镜头记录,用心灵体验 | 订阅本站 | 所有笔记 | 亲和力设计 | 流量

DB2 学习笔记

博客话题:DB2,Linux,Web,业余无线电,户外,摄影,截拳道,Thankpad,其他

软件开发

与软件相关的技术话题,内容比较广泛

在linux 下运行PowerDesigner 15 Beta 2

PowerDesigner 是Sybase 的一款优秀的数据库辅助设计软件,目前没有开发Linux 版本,同时我在Linux 下也找不到可替代的产品。经过尝试,通过wine 可以在Linux 下将PowerDesinger 版本15 很好的运行起来,加上一些额外的努力,还可以启用PowerDesigner 对VB Script 以及数据库连接的支持。我把主要步骤列出来,供有兴趣的朋友参考。 测试环境: Gentoo Linux...

PowerDesigner 是Sybase 的一款优秀的数据库辅助设计软件,目前没有开发Linux 版本,同时我在Linux 下也找不到可替代的产品。经过尝试,通过wine 可以在Linux 下将PowerDesinger 版本15 很好的运行起来,加上一些额外的努力,还可以启用PowerDesigner 对VB Script 以及数据库连接的支持。我把主要步骤列出来,供有兴趣的朋友参考。

测试环境:

  • Gentoo Linux 2.6.24-r3
  • wine-1.1.0
  • PowerDesigner 15 Beta 2

由于缺乏VC80的支持,PowerDesigner 15 Beta 2 的安装程序默认在wine 下面无法正常运行,安装会失败。一种解决方法是移植已有的Windows 安装版本。

移植已有的Windows 安装版本

在Windows 下安装好PowerDesigner,然后将以下目录copy至Linux :

  • 程序的安装目录
  • 将存放License的目录:C:\\Documents and Settings\\All Users\\Application Data\\PowerDesigner 15

然后在Windows 运行注册表,将以下keys 分别导出:

  • [HKEY_CURRENT_USER\Software\Sybase\PowerDesigner 15]
  • [HKEY_LOCAL_MACHINE\SOFTWARE\Sybase\PowerDesigner 15]
  • [HKEY_USERS\.Default\Software\Sybase\PowerDesigner 15]

在Linux 下运行wine regedit 导入以上keys, 如果出错,那么可能需要先对文件的编码进行转换:

$ recode UCS2..  *Sybase*.reg

NOTE:如果没有recode 命令,$ emerge -av1 recode

导入成功之后,在Linux 下运行注册表工具,修改key :[HKEY_LOCAL_MACHINE\SOFTWARE\Sybase\PowerDesigner 15\License\LicenseDirectory],将目录修改为License 在Linux 下存放的目录,例如 "C:\\Documents and Settings\\All Users\\Application Data\\PowerDesigner 15"

如果一切顺利,就可以用以下命令来运行PowerDesigner了。

env WINEPREFIX="$HOME/.wine" wine "C:\Program Files\Sybase\PowerDesigner 15\pdshell15.exe"

另外一种方法是在Linux wine 下安装VC80 支持,然后在Linux 下运行安装程序。

在Linux 下进行安装

NOTE: 命令中跟在#后面的是注释文本 

在运行安装程序之前,按照自己的需求先准备好如下环境:

  • 隔离的wine 环境
  • .Net FrameWork 2.0 Support
  • VB Script Support
    • VBScript 5.6
    • Vitual C++ 2005 support
  • VC80 dll support (必需)
    • Microsoft.VC80.CRT.manifest
    • msvcm80.dll
    • msvcp80.dll
    • msvcr80.dll
  • Connection Profile Support(可在安装完成之后进行)
    • Sun JRE 1.4.2
    • DB2 JDBC Type 4 Driver - db2jcc.jar
隔离的wine 环境

此步为可选。

NOTE:如果省略这一步,请替换下面各节命令中的路径.winePD/ 为.wine/

如果不希望影响原有的wine 环境,或者用于测试,可以为PowerDesigner 新建一个独立的wine 目录。

$ wineprefixcreate --prefix $HOME/.winePD  $ export WINEPREFIX="$HOME/.winePD" 
.Net FrameWork 2.0 Support

这一步是可选。

PowerDesigner 在Beta 2中引入了.Net FrameWork 2.0 支持,用于更漂亮的模型自动排版功能,如果希望使用需要先安装.Net 2.0 ,但.Net 2.0需要IE 5.0以上支持,安装IE 6 可以参考 Wine AppDB - Internet Explorer 6.0,经过尝试是可行的。

IE 6.0安装好之后到MS 网站下载.Net 2.0 安装文件 dotnetfx.exe,放至 $HOME/.winePD/drive_c/,运行安装文件完成安装。

$ wine "c:\dotnetfx.exe"
VB Script Support

这一步是可选。

首先到MS 网站下载合适版本的VB Script 安装文件:WindowsXP-Windows2000-Script56-KB917344-x86-chs.exe

安装过程参考 Wine AppDB - Visual Basic 6.0 Enterprise Edition,所不同之处是安装的文件不一样。

NOTE: 下面的/win/目录是我的 Windows XP安装分区。

$ winecfg # add libraries overwrite; set OS to Windows ME; $ cd .winePD/drive_c/windows/system32/$ for i in riched20.dll riched32.dll oleaut32.dll urlmon.dll hhctrl.ocx ; do mv $i $i.bak; done	$ for i in riched20.dll riched32.dll oleaut32.dll urlmon.dll hhctrl.ocx ; do cp /win/windows/system32/$i .; done$ cp /win/windows/system32/mfc42.dll .winePD/drive_c/windows/system32$ wine "c:\WindowsXP-Windows2000-Script56-KB917344-x86-chs.exe"$ winecfg # set OS back to Windows XP
VC80 dll support

这一步必需完成以运行PowerDesigner 15 Beta 2 安装程序。

在网上下载msvbvm60.dll 和MS-VC80.MSI ,还需要去MS 网站下载Vitual C++ 2005 support 安装文件 vcredist_x86.exe。然后参考以下步骤完成安装。

$  cp Desktop/msvbvm60.dll .winePD/drive_c/windows/system32$  msiexec /i "c:\MS-VC80.MSI"$  wine "c:\vcredist_x86.exe"$  ls .winePD/drive_c/windows/winsxs/ # 确认vcredist_x86.exe 安装成功
Connection Profile Support

这一步是可选。

PowerDesigner 当中支持对数据库的连接,这是通过ODBC 驱动程序实现的,此外,PowerDeisnger也加入了对JDBC 驱动程序的支持,其称作Connection Profile。在Linux 下为wine 程序配置ODBC,我没有经验,可能需要安装MDAC,而通过JDBC则比较容易实现。对于DB2 类型的数据库,IBM 提供一种TYPE 4的JDBC 驱动程序,可以不需要在连接的客户端安装DB2 Client,仅仅拥有TYPE 4驱动程序以及JRE 支持即可。下面的配置就是通过在wine 中配置JRE 以及DB2 JDBC TYPE 4 驱动程序来实现在PowerDesigner 中对DB2 数据库的访问。

NOTE:Linux 世界中的libiodbc 和unixODBC 并不适用这个场景,其提供的ODBC 数据源并不是为wine 程序使用的。

首先需要下载Sun 的JRE安装程序(1.4.2 for windows) 和IBM 的DB2 JDBC TYPE 4 驱动程序(跨平台)。

NOTE:IBM 的JRE 安装程序会检测ibmpc 系统兼容性,我在wine中无法通过检测(甚至在vmware 这样的虚拟机中也不行),具体原因不知道,导致安装失败,因此选择Sun的JRE。

$ winecfg # 在Libraries 中将 urlmon 临时改为 Builtin$ wine "c:\j2re-1_4_2_18-windows-i586-p.exe"$ winecfg # 在Libraries 中将 urlmon 改回 Native$ mkdir ~/.winePD/drive_c/db2java$ cp /opt/IBM/db2/V8.1/java/db2jcc_license_cu.jar ~/.winePD/drive_c/db2java/$ cp /opt/IBM/db2/V8.1/java/db2jcc.jar ~/.winePD/drive_c/db2java/

运行wine regedit,找到key:

[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environmen]

添加或者修改以下2个键值:

CLASSPATH=C:\db2java\db2jcc.jar;C:\db2java\db2jcc_license_cu.jar;. PATH=C:\windows\system32;C:\windows;C:\Program Files\Java\j2re1.4.2_18\bin 
$ regedit #add values to CLASSPATH , PATH
开始安装PowerDesigner 15 Beta 2

下面开始真正安装PowerDesigner。

$ winecfg # 在Libraries 中将 oleaut32 临时改为 Builtin$ wine "c:\PowerDesigner15.exe"$ winecfg # 在Libraries 中将 oldaut32 改回 Native$ wine "C:\Program Files\Sybase\PowerDesigner 15\pdshell15.exe"

整个安装过程应该很顺畅,如果遇到问题,可以重头检查一下必需的步骤。

测试VB Script

执行Tools - Execute Commands - Edit/Run Script,在代码编辑框中写入一句简单的VB Script代码,比如: "dim s",然后点击Run,如果没有任何错误弹出,说明VB Script 支持正常。

测试Connection Profile

在File 菜单中点击 Revers Engineer - Databases,选择IBM DB2 UDB 8.x Common Server - Using a datasource,浏览datasource,选择Connection Profile,Configure 一个新的数据库连接。

点击Test Connection,如果出错,查看output (alt+1)中的messages,有助于解决问题。

TroubleShooting

Could not find dependent assembly L"Microsoft.VC80.CRT" (On running PowerDesigner.exe)

解决方法:安装VC80 dll support。

Runtime error: R6034 - An application has made an attempt to load the C runtime library incorrectly (On running PowerDesigner.exe)

解决方法:安装VC80 dll support。

Failed to load DLL: pdflm15 (On running PowerDesigner.exe)

解决方法:安装VC80 dll support。

err:module:import_dll Library MSVBVM60.DLL (which is needed by ...

解决方法:在Windows 安装中复制msvbvm60.dll 或者网上下载到wine 的system32 目录。

err:module:import_dll Library MFC42.DLL (which is needed by L"C:\\windows\\system32\\vbscript.dll") not found (On Installing VBScript.exe)

解决方法:在Windows 安装中复制mfc42.dll 或者网上下载到wine 的system32 目录。

Required property "serverName" is unknown host

解决方法:检查网络连接,服务器设置,服务器地址,端口,数据库名称等。

Non SQL Error : Could not load class com.ibm.db2.jcc.DB2Driver。

解决方法:检查wine 注册表中CLASSPATH 是否包含正确的db2jcc.jar 全路径,注册表修改之后,应该重新运行PowerDesigner 应用程序。

Could not Initialize JavaVM!

解决方法:检查wine 注册表中PATH 是否包含正确的到JRE bin 路径,注册表修改之后,应该重新运行PowerDesigner 应用程序。

参考资料

延展阅读

 

Posted by Alex at 1:15 AM | Edit | Taged: linux (38), PowerDesigner (4)

mysqld 启动失败一例

启动mysql server 失败,查看/var/log/mysqld.err 080329 16:01:29 [ERROR] Can't start server : Bind on unix socket: No...

启动mysql server 失败,查看/var/log/mysqld.err

080329 16:01:29 [ERROR] Can't start server : Bind on unix socket: No such file or directory
080329 16:01:29 [ERROR] Do you already have another mysqld server running on socket: /var/run/mysqld/mysqld.sock ?
080329 16:01:29 [ERROR] Aborting
080329 16:01:29  InnoDB: Starting shutdown...
080329 16:01:30  InnoDB: Shutdown completed; log sequence number 0 4646872
080329 16:01:30 [Note] /usr/sbin/mysqld: Shutdown complete

百思不得其解,后来查看/var/db/pkg/dev-db/mysql-5.0.56/CONTENTS 文件,或者执行equery f mysql(两种方法非Gentoo Linux 发布不适用),得知安装文件中包含/var/run/mysqld,而这个目录不存在,回忆起之前曾清空过/var/run目录,一直以为这下面的内容都是动态创建,删除之后重启系统即可,看来对mysql 来说还不是这么回事。

创建此目录,并chown,问题解决。

# mkdir /var/run/mysqld
# chown mysql:mysql /var/run/mysqld

Posted by Alex at 8:04 PM | Comments (0) | Edit | Taged: mysql (4)

Gentoo Linux: Subversion

安装 添加需要的USE 标记:(如果愿意加入到/etc/make.conf 中作为全局配置也可以) # echo 'dev-util/subversion svnserve apache2' >> /etc/portage/package.use emerge subversion 服务器与客户端程序: # emerge...

安装

添加需要的USE 标记:(如果愿意加入到/etc/make.conf 中作为全局配置也可以)

# echo 'dev-util/subversion svnserve apache2' >> /etc/portage/package.use

emerge subversion 服务器与客户端程序:

# emerge subversion
[ebuild   R   ] dev-util/subversion-1.4.6-r1  USE="apache2 berkdb emacs java nls perl python ruby svnserve* -bash-completion -debug -doc -extras -nowebdav -vim-syntax" 0 kB  

注意:subversion 依赖于apache 服务,如果系统中没有apache,会自动添加入emerge 列表。

安装好后产生以下主要的文件:

/usr/bin/svnadmin   		用于建立一个repository,也就是一个svn版本库服务
/usr/bin/svn   			svn客户端命令行程序
/etc/init.d/svnserve		svn后台服务程序
/etc/conf.d/svnserve    	svn服务配置文件

配置

默认的配置文件使用/var/svn 作为repository 的根目录,不做修改。

使用root 建立/var/svn 目录,然后建立独立的repository目录: prj。

# mkdir /var/svn
# mkdir /var/svn/prj

现在使用前面创建的路径建立一个repository。

# svnadmin create /var/svn/prj 

这会生成一些目录结构与文件在/var/svn/prj 下面,编辑配置文件/var/svn/prj/conf/passwd,加入svn 授权连接用户与密码。

myusername - mypassword 

接下来编辑repository配置文件/var/svn/prj/conf/svnserve.conf,去除掉以下行的注释:

anon-access = read
auth-access = write
password-db = passwd

注意:行首不要留有空格。 

下面还需要给svn root 目录/var/svn 以及下面的repository 正确的授权,以使以apache用户运行的svnserver 能够正确的存取它们。执行以下命令:
# chown -R apache:root /var/svn 

 现在repository 已经准备好了,可以启动svn 服务,并设置为跟随系统自动启动。

# /etc/init.d/svnserve start
# rc-update add svnserve default 

至此,svn server已经在运行,并且为一个叫做prj的项目建立了一个repository,同时为这个repository设置了连接用户。

下面我们选择一个路径作为保存working copy 的目录,比如位于$HOME ,进入此目录,并首先将prj 项目check out,执行以下命令:

$ cd $HOME
$ svn checkout svn://localhost/prj

第一次执行此命令,svn会提示输入用户名与密码,然后保存在$HOME/.subversion 目录中。以上会在HOME 中目录建立目录$HOME/prj ,并包含$HOME/prj/.svn 用于保存版本控制的文件。

现在可以向$HOME/prj 中copy 项目文件。使用svn add PATH 命令加入版本控制信息,svn -m "message" commit PATH 命令进行提交。

参考信息

Posted by Alex at 2:37 PM | Comments (0) | Edit | Taged: gentoo (21), linux (38), subversion (2), svn (2)

IBM HTTP Download - ibmdl tool

如果经常使用ibm.com或者w3 的HTTP Download 功能,下面的脚本可能会有用,在ibm.com下载的文件名称很多都是采用编码方式,下载得多了从名字很难区分文件内容,一种方法是在IBM HTTP Download 的applet 查看历史记录,另外一种是直接查看IBM HTTP Download 自动维护的dlmgr.pro 文件,这个文件一般与下载文件位于相同的目录。 写了一个简单的脚本ibmdl ,用于列出dlmgr.pro,也可以查询一个文件名,给出内容描述 $...

如果经常使用ibm.com或者w3 的HTTP Download 功能,下面的脚本可能会有用,在ibm.com下载的文件名称很多都是采用编码方式,下载得多了从名字很难区分文件内容,一种方法是在IBM HTTP Download 的applet 查看历史记录,另外一种是直接查看IBM HTTP Download 自动维护的dlmgr.pro 文件,这个文件一般与下载文件位于相同的目录。

写了一个简单的脚本ibmdl ,用于列出dlmgr.pro,也可以查询一个文件名,给出内容描述

$ ibmdl
Usage: ibmdl path [FILE]
Example: 
List all content in ./download/dlmgr.pro :
ibmdl ./download
Query C541WML.tar in ./download/dlmgr.pro :
ibmdl ./download C541WML.tar

Download file

Posted by Alex at 3:08 AM | Comments (0) | Edit | Taged: ibmdl (1)

emacs 学习笔记

如何阅读  以下emacs 命令中 C 代表Contral 键,M 代表Alt 键,ret 是回车。C-x 的含义是按住Contral 再按x 空格用于分隔控制字符和命令,其它字符是emacs 命令,按顺序输入。 尖括号<> 当中的内容不是emacs 命令,按语义输入。...

如何阅读 

以下emacs 命令中

  1. C 代表Contral 键,M 代表Alt 键,ret 是回车。C-x 的含义是按住Contral 再按x
  • 空格用于分隔控制字符和命令,其它字符是emacs 命令,按顺序输入。
  • 尖括号<> 当中的内容不是emacs 命令,按语义输入。
  • 斜杠/用于隔开多个类似的命令和描述。

常用命令

打开文件
C-x f <FilePath> ret
多窗口间切换(窗口1... 窗口N)
C-x 1...<N>
多窗口间切换(上一个/下一个)
C-x 左/右箭头键
关闭当前Buffer 
C-x k ret
保存
C-x C-s
退出
C-x C-c
光标上、下、左、右移动
C-p/C-n/C-b/C-f
光标移动到行首、行尾 
C-a/C-e 
光标前/后移动一个单词
 M-f/M-b
光标前/后移动一个句子
 M-a/M-e
光标前/后移动一个段落
 M-{/M-}
光标到buffer 首/尾
 M-</M->
光标到第N行
 M-g-g N ret
删除后一个字符
 Del/C-d
删除前一个单词
 M-Del/M-Backspace
删除后一个单词
 M-d
删除到句首/尾
 M-k/C-x Backspace/Del
删除到行尾
 
C-k
将当前行与上一行进行合并,空格分隔
M-^ 

类似于vi 的Shift+j ,不过合并的方向相反。

向前/后搜索
C-r/C-s 
向上/下翻页
M-v/C-v 
光标行居中
C-l
选择一个范围 
C-空格

或者

C-2个空格 

上面的命令将当前光标所在位置设为选择起始点,然后移动光标,移动到那就选择到那,直接执行命令即可。后面的用法,会高亮选中的区域,更利于识别。

也可以用鼠标滑亮所选区域。

注:如果输入法占用了"C-空格"的快捷键,最好改成其它。

复制所选文本
 M-w
剪切所选文本
C-w
插入^G或其它控制字符
C-q C-G
dos2unix
C-x ret f unix ret
刷新一个文件内容的tricky 方法(reload)
C-x C-v ret
字符替换
M-x replace-string ret <OldStr> ret <NewStr> ret
替换行尾为换行(正则表达式)
M-x replace-regexp ret $ ret C-q C-J ret

以上2个命令作用范围是当前光标至buffer 末尾。

范围替换
 高亮选中范围 M-x replace-string ret <OldStr> ret <NewStr> ret
列替换
选中范围 C-x r t <NewStr> ret
列删除
选中范围 C-x r d
列剪切 
选中范围 C-x r k
列粘贴
C-x r y
替换Tab为空格
选中范围 M-x untabify ret
删除文件所有行尾空格 
M-x delete-trailing-whitespace ret
批量大小写转换 
选中范围 C-x C-u/C-l
光标后一个单词大小写转换
M-u/M-l
光标所在单词首字母变为大写
M-c
版本比较 diff
C-x v =
版本提交 commit
C-x v v <"message here"> C-c C-c
如果有个emace命令记不起来,可以根据几个字母或者单词搜索,也可用来发现新的命令
C-h a <CmdStr>
更改文件的编码方式

从当前编码变更UTF-8:

C-x ret f utf-8 ret

从当前编码变更到GB2312:

C-x ret f gb2312 ret

这与前面的dos2unix的原理是一样的,在f命令之后可以用TAB键查看编码名称的列表。

需要注意的是,在某些情况下,emacs在保存文件之前会对字符集做些判断,并自动的修改他们,这可能会导致上面的命令事实上的失效。比如,对于一个HTML文档,若在META标签中指定了UTF-8字符集,那么即使你使用上面命令将文件编码修改成任何其它,在emacs执行保存之后,它会自动回到UTF-8编码方式,即使你使用 将META 标签注释也是如此。

查看光标所在那个字符的编码方式
M-x describe-char ret

结果中file code: 后面的内容指出了这个字符在文件中的内码以及编码方式,这里显示的编码方式与emace内部处理文件所采用的编码无关,是文件中真实的、二进制code所用的编码方式。而charset: 后面显示的字符集并不总是与file code一致,似乎这里仅仅表明了与这个字符最亲近的一种字符集。

DB2 相关命令

建立一个db2 进程,并将当前emacs 实例中的buffers 与其关联
M-x sql-db2 ret 
将当前buffer 与emacs实例中已经存在的db2 进程进行关联
M-x sql-set-sqli-buffer ret

在db2 进程已经建立并且关联之后,可以使用以下命令执行db2 CLP命令或SQL语句。

执行整个buffer的语句
M-x sql-send-buffer 
执行选中的语句
选中范围 M-x sql-send-region
执行光标所在段落的语句
M-x sql-send-paragraph

段落的默认分隔符是空行(或者仅含有空格和TAB)。

在命令编辑区编辑新语句并执行
M-x sql-send-string

以上执行语句的部分命令有快捷键可调用,也可以使用global-set-key 命令将其更改为自己习惯的按键。

emacs环境配置

将emacs加入Windows 右键菜单

为了能使用鼠标右键菜单,快捷的将任何文件以emacs方式打开,可以通过以下两种方式实现:

  • 使用"发送到(Send to)

为runemacs.exe 创建一个快捷方式,并放至"发送到(Send to)"文件夹,一般位于:"C:\Documents and Settings\***\SendTo"。

  • 右键菜单直接打开

这种方式需要修改注册表,参照以下注册表内容,可以修改路径后导入,也可以独立创建注册表项目

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\shell\Open with emacs]

[HKEY_CLASSES_ROOT\*\shell\Open with emacs\command]
@="C:\\emacs-23.1\\bin\\runemacs.exe\" \"%1\""

这种方式会为每个文件创建一个单独的编辑窗口(多个emacs实例),若希望共享一个emacs编辑窗口(共享emacs实例),请参照下面一个小节。

更友好的右键菜单

为了实现多次打开的文件共享同一个emacs编辑窗口,需要以下步骤:

  1. 将以下命令加入.emacs文件(一般位于: "C:\Documents and Settings\***\Application Data\.emacs")
    (server-start)
  2. 新建以下Windows 环境变量:
    EMACS_SERVER_FILE=C:\Documents and Settings\***\Application Data\.emacs.d\server\server
    在完成第一步以后,启动emacs,查看以上环境变量路径中的文件是否创建成功,若没有找到此文件,请核对你的emacs版本修改为正确的路径。以上文件在emacs关闭后自动删除。
    环境变量设置完毕需要重新启动Windows才能生效。
  3. 修改注册表,添加右键菜单项。若前面已经创建此注册表项目,对照这里修改即可。
    Windows Registry Editor Version 5.00
    
    [HKEY_CLASSES_ROOT\*\shell\Open with emacs]
    
    [HKEY_CLASSES_ROOT\*\shell\Open with emacs\command]
    @="C:\\emacs-23.1\\bin\\emacsclientw.exe -a \"C:\\emacs-23.1\\bin\\runemacs.exe\" \"%1\""
    emacsclientw.exe 将不启动新的实例,而是在当前实例中打开新的文件,而"-a"参数通知emacs若没有实例在运行,则启动新的实例runemacs.exe。
    这个方式简单有效,可以满足大部分需求,但有一个缺陷,即在没有任何emacs实例运行的情况下,一次选中多个文件打开时,会打开等同于文件数目的emacs窗口,即第一次无效。原因是打开文件的请求是同时处理的,第一个emacs实例创建的服务(前述的server文件)还来不及被检测到。
    解决这个问题要稍微复杂点,请参考接下来的小节。

完美的右键菜单
若希望在任何时候发送到emacs的多个文件都只会打开一个实例,所需的工作会有点难看,不过是有效的,会用到DOS batch文件和vbs文件,DOS batch文件用来检测已运行的emacs实例,并等待服务文件创建完毕再继续文件打开操作。而vbs文件用来调用DOS batch文件以隐藏DOS窗口。
  1. 按照上一小节的步骤1与步骤2,配置好.emacs文件与环境变量EMACS_SERVER_FILE
  2. 下面是两个文件(emacs.bat 与emacs.vbs)的代码,保存到emacs的bin目录。
  3. emacs.bat

    tasklist /FI "IMAGENAME eq emacs.exe" /NH | find "emacs.exe" && goto check || goto new
    :new
    C:\emacs-23.1\bin\runemacs.exe
    :check
    if not exist "%EMACS_SERVER_FILE%" goto check
    :existing
    C:\emacs-23.1\bin\emacsclientw.exe -n %1
    emacs.vbs

    DIM objShell
    set objShell=wscript.createObject("wscript.shell")
    iReturn=objShell.Run("cmd.exe /C C:\emacs-23.1\bin\emacs.bat " & """" & WScript.Arguments(0) & """", 0, TRUE)
  4. 修改注册表,添加右键菜单项。若前面已经创建此注册表项目,对照这里修改即可。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell\Open with emacs]
[HKEY_CLASSES_ROOT\*\shell\Open with emacs\command]
@="Wscript.exe \"C:\\emacs-23.1\\bin\\emacs.vbs\" \"%1\""
经过以上修改,emacs右键菜单在第一次启动emacs时,也可以将多个文件共享在一个实例中打开了。

延展阅读

Posted by Alex at 8:55 PM | Comments (0) | Edit | Taged: emacs (1), Study notes (10)

Subversion 安装笔记

subversion 是一个开源的版本控制软件,虽然在稳定性方面较cvs还有些距离,不过很受追捧,牛顿顿说,这主要得益于subversion在处理大项目效率方面的优势,目前版本是1.4.4。 安装subversion 服务 在这里 下载subversion 的windows 平台软件包,解压缩即可。 然后打开一个DOS 命令行,进入到解压缩的bin目录,运行以下命令创建一个repository(存放版本控制信息的地方)。把PROJECT_NAME 替换为你需要的项目名称。 C:\svn\bin> mkdir \repository\subversion\PROJECT_NAME C:\svn\bin>...

subversion 是一个开源的版本控制软件,虽然在稳定性方面较cvs还有些距离,不过很受追捧,牛顿顿说,这主要得益于subversion在处理大项目效率方面的优势,目前版本是1.4.4。

安装subversion 服务

这里 下载subversion 的windows 平台软件包,解压缩即可。

然后打开一个DOS 命令行,进入到解压缩的bin目录,运行以下命令创建一个repository(存放版本控制信息的地方)。把PROJECT_NAME 替换为你需要的项目名称。

C:\svn\bin> mkdir \repository\subversion\PROJECT_NAME
C:\svn\bin> svnadmin create \repository\subversion\PROJECT_NAME

然后参照这里的安装指南 把subsersion安装为windows系统服务,并在启动时加载。意等号后面必须留有空格。

C:\svn\bin> sc create svn binpath= "C:\svn\bin\svnserve.exe --service 
-r C:\repository\subversion" displayname= "Subversion Server" 
depend= Tcpip start= auto

注意:执行命令时,需要把subversion 的安装路径以及repository 的路径替换成你自己的路径。svn 是服务名称,你也可以随便命名。

这个系统服务可以通过 sc delete svn 命令删除,服务的启动有两种方式:sc start svn 或者 net start svn 。具体可以参考windows 的sc  以及net 命令。

配置版本库权限

配置文件位于 .\repository\subversion\PROJECT_NAME\conf\ 目录

passwd 文件定义了存取版本库的用户名与密码,格式为:

myusername - mypassword 

svnserve.conf 文件需要添加如下几行:

anon-access = noneauth-access = writepassword-db = passwd 

如够希望匿名用户可以checkout 版本库,那么修改第一行为 anon-access = read。 下面一行用于复杂一些的权限配置,如果添加此行,需要配置authz 文件,在这里我把此行注释掉。

# authz-db = authz 

另外还有一行用于定义Project名称,但经过实验,这行可以省略。

# realm = PROJECT_NAME 

至此,subversion 服务安装完成,并且创建了一个版本库。如果已经安装了subversion客户端,则可以通过svn://localhost/PROJECT_NAME 进行访问。

subversion 客户端

subversion 的客户端在windows 可以使用tortoiseSVN 或者subversion eclipes plugin. 

tortoiseSVN 是一个基于subversion服务的windows GUI,并集成了tortoiseMerge 版本比较工具。它和Windows 资源管理器紧密地结合在一起,以插件形式出现,来进行版本控制操作,当前版本也是1.4.4。

使用subversion eclipes plugin 需要先安装eclipes,然后通过插件更新安装subversion plugin,安装完成后,就可以像使用cvs 一样在eclipes 中使用subversion。

在这里下载 tortoiseSVN 1.4.4,安装完成之后,在资源管理器中点击右键,就会看到相关菜单。可以在资源管理器找到要管理的源代码,在目录上点击右键,选择TortoiseSVN 菜单中的Import ,并在Url of repository 下来菜单中选择刚才创建好的repository 即可。然后在working copy 目录中将代码checkout,就在working copy 目录中实现了代码的版本管理。

版本库备份

如果需要对项目版本库备份,只需要把整个.\repository\subversion\PROJECT_NAME 目录备份即可,如果需要在另一台还原备份,需要先安装好subversion服务与客户端(不必创建repository),然后把上述目录copy到svn服务所引用的目录中即可(本例为 c:\repository\subversion)。然后刷新subversion client,通过认证即可看到版本库。

参考信息

Posted by Alex at 8:11 PM | Comments (0) | Edit | Taged: subversion (2), svn (2), tortoiseSVN (1)

Firefox Extension 思维导图

因为图片太宽,要完整显示,请点击这里暂时禁止样式单。 恢复样式单,请点击这里,或者刷新本页。 最近几天看有关Firefox Extension 的开发,有很多知识链接,使用MindManager 做了这个思维导图,链接可以点击。 更新了链接,增加了浏览指引小图标...

因为图片太宽,要完整显示,请点击这里暂时禁止样式单

恢复样式单,请点击这里,或者刷新本页。

Firefox Extension Why use XUL? Why use XUL? http://www.mozilla.org/js/spidermonkey/ http://www.mozilla.org/js/spidermonkey/ http://kb.mozillazine.org/Setting_up_extension_development_environment http://kb.mozillazine.org/Setting_up_extension_development_environment http://xulplanet.com/tutorials/xultu/ http://xulplanet.com/tutorials/xultu/ http://developer.mozilla.org/en/docs/About_JavaScript http://developer.mozilla.org/en/docs/About_JavaScript http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide http://www.cat-snow.com/post/146.html http://www.cat-snow.com/post/146.html http://www.cat-snow.com/post/137.html http://www.cat-snow.com/post/137.html http://www.xulplanet.com/references/elemref/ http://www.xulplanet.com/references/elemref/ http://www.cat-snow.com/post/140.html http://www.cat-snow.com/post/140.html http://www.cat-snow.com/post/145.html http://www.cat-snow.com/post/145.html http://www.mozilla.org/js/scripting/ http://www.mozilla.org/js/scripting/ http://kb.mozillazine.org/Extension_development http://kb.mozillazine.org/Extension_development http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference http://developer.mozilla.org/en/docs/Creating_Custom_Firefox_Extensions_with_the_Mozilla_Build_System http://developer.mozilla.org/en/docs/Creating_Custom_Firefox_Extensions_with_the_Mozilla_Build_System http://developer.mozilla.org/en/docs/Extensions http://developer.mozilla.org/en/docs/Extensions http://developer.mozilla.org/en/docs/XMLHttpRequest http://developer.mozilla.org/en/docs/XMLHttpRequest http://developer.mozilla.org/en/docs/Building_an_Extension http://developer.mozilla.org/en/docs/Building_an_Extension http://developer.mozilla.org/en/docs/Code_snippets http://developer.mozilla.org/en/docs/Code_snippets

最近几天看有关Firefox Extension 的开发,有很多知识链接,使用MindManager 做了这个思维导图,链接可以点击。

更新了链接,增加了浏览指引小图标

Posted by Alex at 2:43 AM | Edit | Taged: Firefox (7)

Javascript 的噩梦可以结束了

Firebug 是针对Firefox 的一个插件,它与Firefox 很好的集成到了一起,帮助你分析与网页在客户端的一切行为,当然,获益最大的大概就是Javascript 的调试了。 下面是一个例子,红色部分代表遇到了一个脚本错误。 点开后,Firebug 指示出了错误文件与相关代码行。  再继续查看,就直接定位到了相关上下文,这里我们可以看到,是在line 22 多了一个额外的注释标记 "*/",导致此错误。 修复后,显示正常状态。 如果错误不太容易找到,还可以设置断点进行调试,直接在行号部分点击即可设置断点,然后刷新一次网页重新执行脚本,执行到断点处会自动停下,可以像通常调试工具一样选择执行进度,观察并设置变量。 Firebug...

Firebug 是针对Firefox 的一个插件,它与Firefox 很好的集成到了一起,帮助你分析与网页在客户端的一切行为,当然,获益最大的大概就是Javascript 的调试了。

下面是一个例子,红色部分代表遇到了一个脚本错误。

firebug status

点开后,Firebug 指示出了错误文件与相关代码行。

firebug interface

 再继续查看,就直接定位到了相关上下文,这里我们可以看到,是在line 22 多了一个额外的注释标记 "*/",导致此错误。

firebug interface

修复后,显示正常状态。

firebug status

如果错误不太容易找到,还可以设置断点进行调试,直接在行号部分点击即可设置断点,然后刷新一次网页重新执行脚本,执行到断点处会自动停下,可以像通常调试工具一样选择执行进度,观察并设置变量。

Firebug 是完全免费的,要了解更多的功能,请参考考官方网站 。 

Posted by Alex at 1:00 AM | Comments (0) | Edit | Taged: debug (1), Firefox (7), Javascript (5)

Google 之三人成虎

这两天查找有关彻底清除硬盘数据的信息,Google帮了不少忙,很多资料都引用了美国国防部的DoD 5220.22-M 标准,类似以下的文字在中文互联网上广泛流传 。 美国国防部在《国家工业安全程序操作手册》中对硬盘数据清除作了专门的描述,这个安全标准被称为DOD 5220.22-M(www.dss.mil/isec/nispom_0195.htm)。该手册建议对所要清除的数据区进行三次覆盖,第一次使用一个8位的字符,第二次使用该字符的互补字符(即将二进制位的0、1互换),最后再使用随机字符进行覆盖。 注:NISPOM 是国家工业安全程序(NATIONAL INDUSTRIAL SECURITY PROGRAM)的缩写。 国内外的各种数据清除工具,也有很多按上述类似说法引用了此标准,比如有名的开源软件Darik's Boot and Nuke。出于兴趣,我找到了这个...

这两天查找有关彻底清除硬盘数据的信息,Google帮了不少忙,很多资料都引用了美国国防部的DoD 5220.22-M 标准,类似以下的文字在中文互联网上广泛流传

美国国防部在《国家工业安全程序操作手册》中对硬盘数据清除作了专门的描述,这个安全标准被称为DOD 5220.22-M(www.dss.mil/isec/nispom_0195.htm)。该手册建议对所要清除的数据区进行三次覆盖,第一次使用一个8位的字符,第二次使用该字符的互补字符(即将二进制位的0、1互换),最后再使用随机字符进行覆盖。

注:NISPOM 是国家工业安全程序(NATIONAL INDUSTRIAL SECURITY PROGRAM)的缩写。

国内外的各种数据清除工具,也有很多按上述类似说法引用了此标准,比如有名的开源软件Darik's Boot and Nuke。出于兴趣,我找到了这个 DoD 5220.22-M 文档(前文的链接无法访问),但无论如何有没有找到被大量资料所提及的数据清除方法。我开始怀疑这个引用的权威性,最后,在wikipedia 找到的文字证实了我的猜测。国内一直无法访问wikipedia ,下面的链接使用了网站代理

Misunderstanding as a data sanitization standard

DoD 5220.22-M is sometimes cited as a standard for sanitization to counter data remanence. This is incorrect. The NISPOM covers the entire field of government-industrial security, of which data sanitization is a very small part (about two paragraphs in a 100 page document)[4]. Furthermore, the NISPOM does not actually specify any particular method. Standards for sanitization are left up to the Cognizant Security Authority. The Defense Security Service provides a Clearing and Sanitization Matrix[5] which does specify methods; access to the current C&SM is restricted here.

DoD 5220.22-M 一度被作为数据清除的标准进行引用,这是错误的。这个文档覆盖了全部的政府与工业的安全领域,描述数据清除的内容仅占很小的一部分(100页文档中仅有2段文字)。此外,文档中并没有定义任何具体的数据清除方法。数据清除的标准是由Cognizant Security Authority(不知如何翻译)来决定的,可以在国防安全服务(Defense Security Service)提供的一个名为“Clearing and Sanitization Matrix的文档中找到。

以下是有关硬盘数据的那部分内容,与前述相符,但不同的是,这不属于DoD 5220.22-M 标准。

d. THIS METHOD NOT APPROVED FOR SANITIZING MEDIA THAT CONTAINS TOP SECRET INFORMATION.

1. Before any sanitization product is acquired, careful analysis to the overall costs associated with overwrite/sanitization should be made. Depending on the contractor’s environment, the size of the drive and the differences in the individual products time to perform the sanitization, destruction of the media might be the preferred (i.e., economical) sanitization method.

2. Overwrite all addressable locations with a character, then its complement. Verify “complement” character was written successfully to all addressable locations, then overwrite all addressable locations with random characters; or verify third overwrite of random characters. Overwrite utility must write/read to “growth” defect list/sectors or disk must be mapped before initial classified use and remapped before sanitization. Difference in the comparison lists must be discussed with the DSS Industrial Security Representative (IS Rep) and/or Information System Security Professional (ISSP) before declassification. Note: Overwrite utilities must be authorized by DSS before use.

值得注意的是,文档中注明:这种数据清除方法并不适用于绝密信息。可见对于美国以外,甚至其它非政府控制机构,其安全性仍然有限。

Google就像快餐,速食并且方便,但食得不干净却容易拉肚,很多人不再关心信息的来源,瞧!Google到这么多相同的搜索结果,就是它了!正所谓三人成虎。

Posted by Alex at 11:17 PM | Comments (0) | Edit | Taged: Google (3), 硬盘 (2)

硬盘:一次写入,永不消失

像大多数人所了解的,我知道计算机内部使用二进制1和0传输和存储数据,硬盘也不例外,磁盘片上布满了用磁格式记录的1和0。这些1和0是磁盘上数据的最小组成单位,数据包括文件分配表(FAT)以及实际文件内容等。从前我认为,删除文件之后(在文件分配表中删除),只要利用工具使用其它数据覆写一遍原文件所在区域(实际存储区域),就会使这个文件无法恢复。 但实际情况可不是这么简单,磁盘上记录了代表1和0的磁信号,对个人计算机上的磁头来说,这是数字信号,但对于专用的设备,它们却还是模拟信号,每一个数据位磁信号的强弱,或者数据位深层遗留的磁信号,以及由于定位误差在数据位边缘留下的信号(称为影子数据),在这样的设备看来,都是线形的。 目前网上广泛流传的说法是,使用专用的设备可以把覆写过多次的硬盘一遍又一遍的依次还原回来,理论上可以高达7次。 当然这种方法的成本也很高,可能一般只用于解决国家安全级别的问题。 美国国防安全服务(Defense Security Service)在一个名为“Clearing and Sanitization Matrix ”的文档中,定义了一般密级数据的清除标准,但并不适用于绝密数据。有关这个标准,网上一直存在着广泛的误解,我在“Google 之三人成虎 ”文中予以了澄清。 对于硬盘的一般性物理损坏,以及非彻底消磁等手段,也无法保证数据的安全性,在现今普遍的高密度磁记录水平下,一小片底层仍含有微弱磁信号的磁盘碎片足以包含大量的敏感信息,并可能被恢复。 前述的理论已经形成多年,在技术日益更新的今天,发达国家当今最先进的数据恢复技术如何,还不得而知,但我们可以想象,对于一个普通用户来说,“一次写入,永不消失”这个说法一点也不为过。...

Destroy your data像大多数人所了解的,我知道计算机内部使用二进制1和0传输和存储数据,硬盘也不例外,磁盘片上布满了用磁格式记录的1和0。这些1和0是磁盘上数据的最小组成单位,数据包括文件分配表(FAT)以及实际文件内容等。从前我认为,删除文件之后(在文件分配表中删除),只要利用工具使用其它数据覆写一遍原文件所在区域(实际存储区域),就会使这个文件无法恢复。

但实际情况可不是这么简单,磁盘上记录了代表1和0的磁信号,对个人计算机上的磁头来说,这是数字信号,但对于专用的设备,它们却还是模拟信号,每一个数据位磁信号的强弱,或者数据位深层遗留的磁信号,以及由于定位误差在数据位边缘留下的信号(称为影子数据),在这样的设备看来,都是线形的。

目前网上广泛流传的说法是,使用专用的设备可以把覆写过多次的硬盘一遍又一遍的依次还原回来,理论上可以高达7次。 当然这种方法的成本也很高,可能一般只用于解决国家安全级别的问题。

美国国防安全服务(Defense Security Service)在一个名为“Clearing and Sanitization Matrix的文档中,定义了一般密级数据的清除标准,但并不适用于绝密数据。有关这个标准,网上一直存在着广泛的误解,我在“Google 之三人成虎 ”文中予以了澄清。

对于硬盘的一般性物理损坏,以及非彻底消磁等手段,也无法保证数据的安全性,在现今普遍的高密度磁记录水平下,一小片底层仍含有微弱磁信号的磁盘碎片足以包含大量的敏感信息,并可能被恢复。

前述的理论已经形成多年,在技术日益更新的今天,发达国家当今最先进的数据恢复技术如何,还不得而知,但我们可以想象,对于一个普通用户来说,“一次写入,永不消失”这个说法一点也不为过。

煎蛋 的blog找到这个很酷的图片用作本文配图。

DoD 是Department of Defense(国防部) 的缩写,这里指美国国防部。

Reference 

Posted by Alex at 3:09 PM | Comments (0) | Edit | Taged: 硬盘 (2)

UltraEdit 对UTF-8文件的自动处理

关于Unicode 编码,这里有篇文章 可以参考。Unicode是几种多字节编码格式的统称,其中使用广泛的有UTF-8与UTF-16,而通常人们说Unicode时,实际是在指UTF-16编码,UltraEdit 就是这样,为了避免混淆,下面都按照标准引用名字。  使用UltraEdit 打开一个UTF-8 编码的文件,然后按ctrl+h 进入16进制模式查看文件内码,你会发现文件已经被转换成UTF-16 编码,并添加了UTF-16 little endian 的BOM :FF  FE,UltraEdit 状态栏文件的尺寸也增加了不少。其实大可不必担心,可以回到正常模式继续编辑并保存,保存后的文件并没有改变编码格式,只是在编辑时使用UTF-16格式而已。...

关于Unicode 编码,这里有篇文章 可以参考。Unicode是几种多字节编码格式的统称,其中使用广泛的有UTF-8与UTF-16,而通常人们说Unicode时,实际是在指UTF-16编码,UltraEdit 就是这样,为了避免混淆,下面都按照标准引用名字。 

使用UltraEdit 打开一个UTF-8 编码的文件,然后按ctrl+h 进入16进制模式查看文件内码,你会发现文件已经被转换成UTF-16 编码,并添加了UTF-16 little endian 的BOM :FF  FE,UltraEdit 状态栏文件的尺寸也增加了不少。其实大可不必担心,可以回到正常模式继续编辑并保存,保存后的文件并没有改变编码格式,只是在编辑时使用UTF-16格式而已。

因此,如果通过UltraEdit打开文件查看BOM 来确定文件格式,并不是安全的。UltraEdit 下方状态栏则真实的显示了当前打开文件的实际编码格式,而不是当前编辑的编码格式。 对于一个普通Ascii 格式的文件,它显示为DOS 或者UNIX,对于一个包含有UTF-8编码字符的文件,它显示为U8-DOS 或者U8-UNIX,对于UTF-16编码的文件,它显示为U-DOS 或者U-UNIX。

我们知道,UTF-8 对于Ascii 字符的编码与原有的Ascii 编码一致,因此假如我们删除了一个UTF-8 DOS文件中所有Ascii 以外的字符,保存后再打开,UltraEdit 将显示为DOS(Ascii)。

如果我们不希望UltraEdit 在打开UTF-8 文件时自动转为UTF-16 格式编辑,我们可以修改配置。如下图,确保“自动检测 UTF-8文件”不被选中。

 UltraEdit configuration

需要注意的是,如果取消了这个选项,UltraEdit打开包含UTF-8编码的文件会产生乱码。

UltraEdit 在File-Convertions 菜单中提供了多种编码格式之间的转换,这将影响到保存的文件编码,转换后,在状态栏也能看到相应变化。在有些选项后标明有(Unicode Editing) 或者(ASCII Editing),这指定了编辑时显示用的编码,并不影响保存文件所用的编码,要区分开。

工具WinHex 可以用来查看文件16进制内码。

Posted by Alex at 1:02 AM | Comments (0) | Edit | Taged: UltraEdit (3), unicode (3), UTF-16 (1), utf-8 (3), 编码 (1)

在Perl 中如何按字节操作字符串

从版本5.6,Perl 内部开始支持Unicode编码,一些字符串操作函数,比如length、substr,默认基于字符操作,而不是bytes 操作。在Perl中,如果希望使substr() 函数基于bytes 操作怎么办, 在Oracle 8i数据库中有个对应的函数,叫做substrb(),在DB2 8.2中,字符串函数默认就是基于bytes 操作,并不识别字符编码。而对于perl,没有提供相应的函数,而是用了另一种方法,bytes 关键字,用法如下: use bytes; substr(); no bytes;...

从版本5.6,Perl 内部开始支持Unicode编码,一些字符串操作函数,比如length、substr,默认基于字符操作,而不是bytes 操作。在Perl中,如果希望使substr() 函数基于bytes 操作怎么办, 在Oracle 8i数据库中有个对应的函数,叫做substrb(),在DB2 8.2中,字符串函数默认就是基于bytes 操作,并不识别字符编码。而对于perl,没有提供相应的函数,而是用了另一种方法,bytes 关键字,用法如下:

use bytes;
substr();
no bytes;

更多信息,请参考Perl 文档。 

Posted by Alex at 6:31 PM | Comments (0) | Edit | Taged: perl (2), 字符 (1), 字节 (1)

在Dreamhost自动备份mysql数据库

今天准备在Dreamhost 上自动备份blog的DB,MySQL是Dreamhost唯一提供的关系数据库服务,当前版本5.0 我了解很少(这里有个学习笔记 ),已知可以在命令行使用mysqldump 命令备份整个数据库,备份时似乎无需了解数据库的当前状态(比如是否正在运行事务),它会自行处理,从备份出的文件看,mysqldump实现的是冷备份,也就是说,仅采用这种备份方法,数据仅能恢复到最后一次运行mysqldump的时间点。 MySQL也介绍了一个热备份工具mysqlhotcopy,不过文档中有以下声明,我不清楚什么是MyISAM 与ARCHIVE tables,也不大明白MySQL 热备份的恢复机制(如何前滚日志)。还好,我的数据库目前很小,而我的空间很大很大,每次全备份只花费很少的时间,也占用很小的空间。 mysqlhotcopy works only for backing up...

今天准备在Dreamhost 上自动备份blog的DB,MySQL是Dreamhost唯一提供的关系数据库服务,当前版本5.0 我了解很少(这里有个学习笔记 ),已知可以在命令行使用mysqldump 命令备份整个数据库,备份时似乎无需了解数据库的当前状态(比如是否正在运行事务),它会自行处理,从备份出的文件看,mysqldump实现的是冷备份,也就是说,仅采用这种备份方法,数据仅能恢复到最后一次运行mysqldump的时间点。

MySQL也介绍了一个热备份工具mysqlhotcopy,不过文档中有以下声明,我不清楚什么是MyISAM 与ARCHIVE tables,也不大明白MySQL 热备份的恢复机制(如何前滚日志)。还好,我的数据库目前很小,而我的空间很大很大,每次全备份只花费很少的时间,也占用很小的空间。

mysqlhotcopy works only for backing up MyISAM and ARCHIVE tables. It runs on Unix and NetWare.

使用mysqldump备份出来的文件,是一个包含了sql语句的文本文件,含有建表语句以及Insert语句。我采用这种方式恢复:连接到mysql进入mysql提示符,创建一个db(如果不存在),连接到此db,执行备份文件,如:

mysql> source ./mybackup.sql 

执行结束则恢复完毕。这里有篇日志 介绍了另外一种恢复方法(如下),不过我没有尝试过。 

$ mysqldump -u root -p'123456' test2 < samp.db.txt 

言归正传,我准备采用crontab 自动运行mysqldump 来自动备份数据库。写了一个shell脚本如下,备份文件使用当前系统时间(到分钟)作为文件名,备份成功后进行压缩。

backupdb.sh 

bakfile=~/xxx/mt-db_`date '+\%Y\%m\%d\%H\%M'|tr -d '\\\'`.sql
mysqldump -hmysql.mydomain.com -umyname -pmypasswd mt > $bakfile
gzip $bakfile
exit 0

然后在crontab中加入条目,定时自动运行此脚本。这里设置为每4个小时,在整点运行一次,每天产生一份日志。

00 0-23/4 * * * ~/xxx/backupdb.sh >> ~/xxx/logs/backupdb_`date '+\%Y\%m\%d'|tr -d '\\\'`.log 

经试验`date '+\%Y\%m\%d'|tr -d '\\\'`部分似乎不能被cron命令成功替换,但是在shell中是可以成功执行的。

$ echo backupdb_`date '+\%Y\%m\%d'|tr -d '\\\'`.log
backupdb_20070127.log

Posted by Alex at 2:09 AM | Comments (1) | Edit | Taged: backup (2), Database (12), Dreamhost (4), mysql (4), 备份 (1)

MySQL Notes

Connect to db mtmysql> use mtList DB or tablesmysql> show databases;mysql> show tables;Backup DB mtdos> mysqldump...

Connect to db mt

mysql> use mt

List DB or tables

mysql> show databases;
mysql> show tables;

Backup DB mt

dos> mysqldump -u root -p mt > dbname.sql;

Query table mt_entry structure 

mysql> show create table mt_entry;

 

 

Posted by Alex at 3:46 AM | Comments (0) | Edit | Taged: mysql (4), Study notes (10)

Convert MT from Berkeley DB to MySQL

最近决定把MT的后台数据从Berkeley的文件DB转到MySQL。原因之一是使用关系数据库可以获得更多的灵活性,比如运行一条sql来变更 所有entry的某一个属性;另外一个原因是为了提前熟悉一下这个数据库,牙牙网站使用虚拟主机,DreamHost提供的数据库是MySQL,而我从前 主要使用了SQL Server,Oracle和DB2,Postgresql也只了解了一点,而MySQL则几乎没有接触过。 下载了5.0.27安装文件,安装顺利,使用了第一个默认端口3306,UTF-8的数据库字符集。 安装完成,运行了命令行客户端,输入help,给出的帮助命令不多,也没见到有关数据库创建的命令,便查了安装的帮助文件,组织的还算不错,很齐全,很快找到相关的命令: 查看已有数据库 show databases; 连接数据库 test use test; 创建数据库 mt...

最近决定把MT的后台数据从Berkeley的文件DB转到MySQL。原因之一是使用关系数据库可以获得更多的灵活性,比如运行一条sql来变更 所有entry的某一个属性;另外一个原因是为了提前熟悉一下这个数据库,牙牙网站使用虚拟主机,DreamHost提供的数据库是MySQL,而我从前 主要使用了SQL Server,Oracle和DB2,Postgresql也只了解了一点,而MySQL则几乎没有接触过。

下载了5.0.27安装文件,安装顺利,使用了第一个默认端口3306,UTF-8的数据库字符集。

安装完成,运行了命令行客户端,输入help,给出的帮助命令不多,也没见到有关数据库创建的命令,便查了安装的帮助文件,组织的还算不错,很齐全,很快找到相关的命令:

查看已有数据库
show databases;
连接数据库 test
use test;
创建数据库 mt
create database mt;

接下来寻找把已有DB转化到MySQL的方法,很快找到一个第三方的工具mt-db-convert.cgi ,可以实现MT支持的几种不同的DB之间的转换。

下载回来放到cgi-bin的mt目录,在浏览器访问运行,输入新创建的MySQL数据库名称mt,用户名root,密码,主机填入localhost,开始Convert。转换过程很快就完成了,不到1分钟,没有出现错误提示,转换信息如下:

mt-db-convert.cgi($Rev: 173 $): Converting your MT data between DB engines (for MT 3.2)
Loading database schema...                                                                                    
Loading data...                                                                                               
MT::Author                                                                                                    
.                                                                                                             
(1 objects saved.)                                                                                            
MT::Blog                                                                                                      
..                                                                                                            
(2 objects saved.)                                                                                            
MT::Trackback                                                                                                 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......                                                                                 
(327 objects saved.)                                                                                          
MT::Category                                                                                                  
.......... .......... .......... .......... ..........                                                        
(50 objects saved.)                                                                                           
MT::Comment                                                                                                   
..                                                                                                            
(2 objects saved.)                                                                                            
MT::Entry                                                                                                     
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... ....                                                                                               
(514 objects saved.)                                                                                          
MT::IPBanList                                                                                                 
(0 objects saved.)                                                                                            
MT::Log                                                                                                       
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......                                                                      
(437 objects saved.)                                                                                          
MT::Notification                                                                                              
(0 objects saved.)                                                                                            
MT::Permission                                                                                                
....                                                                                                          
(4 objects saved.)                                                                                            
MT::Placement                                                                                                 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......... .......... .......... .......... .......... .......... .......... .......... 
.......... .......... .......                                                                                 
(527 objects saved.)                                                                                          
MT::Template                                                                                                  
.......... .......... .......... .......... .......... .......... .......... .......... ...                   
(83 objects saved.)                                                                                           
MT::TemplateMap                                                                                               
..........                                                                                                    
(10 objects saved.)                                                                                           
MT::TBPing                                                                                                    
(0 objects saved.)                                                                                            
MT::Session                                                                                                   
.......... .......... .........                                                                               
(29 objects saved.)                                                                                           
MT::PluginData                                                                                                
(0 objects saved.)                                                                                            
MT::Config                                                                                                    
.                                                                                                             
(1 objects saved.)                                                                                            
MT::FileInfo                                                                                                  
(0 objects saved.)                                                                                            
Done copying data from DBM to DBI::mysql! All went well.                                                      
Your recommended setting                                                                                      
-------------------------------------                                                                         
# DataSource R:/SITE/mt-db                                                                                    
ObjectDriver DBI::mysql                                                                                       
Database mt                                                                                                   
DBUser root                                                                                                   
DBHost localhost                                                                                              
DBPassword comein                                                                                             
-------------------------------------                                                                         

然后修改mt-config.cgi文件,把数据源改成MySQL。 

再次访问MT,看起来一切正常,甚至原有的session都没有丢掉,只是后台使用的数据已经改变了位置。
不过发现了中文乱码问题,Google到了这个方法 ,按文修改后,需要再次执行mt-db-convert.cgi,把数据重新导入MySQL,再访问MT,乱码没有了,所有中文显示正常。

转换后又发现了一个新的问题,原DB中的Tags全部丢失,原因是mt-db-convert.cgi仅支持到MT 3.1,3.3的新功能带来的数据变化可能还会有其它信息丢失。

NOTE

This script is compatible with Movable Type version 3.1x. I didn't test it at any other versions.


其实MT自身已经提供了转换工具mt-db2sql.cgi,与mt.cgi在同一目录当中。使用方法是,安装好MySQL数据库服务,并创建好用于MT的DB,将MySQL数据库的配置信息加入mt-config.cgi,并同时保留原Berkeley DB的路径配置,即保持两种DB的配置同时生效,然后通过浏览器运行mt-db2sql.cgi,转换会自动开始,显示如下信息:

Loading database schema...
Loading data...
MT::Author
1
MT::Blog
1
3
MT::Trackback 
...
Done copying data from Berkeley DB to your SQL database! All went well.

确认转换成功后,再修改mt-config.cgi,注释掉Berkeley DB的配置即可。

这里是官方的帮助文档:

 

Posted by Alex at 2:35 AM | Edit | Taged: MT (25), mysql (4)

Unicode Byte Order Mark

Unicode file always set the first byte as certain content to indicate the file format. We...

Unicode file always set the first byte as certain content to indicate the file format. We could use edit tools like UltraEdit open the file in Hex mode to see the byte.

Byte order mark Description
EF BB BF UTF-8
FF FE UTF-16, little endian
FE FF UTF-16, big endian
FF FE 00 00 UTF-32, little endian
00 00 FE FF UTF-32, big-endian

Note: Microsoft uses UTF-16, little endian byte order.

More refer to Byte-order Mark

 

Posted by Alex at 4:54 PM | Comments (0) | Edit | Taged: unicode (3)

batch for forlders synchronization

 synfolders.bat@echo onrem Save the current dir, [d] is disk letter, [p] is path namepushd %~dp0rem Change...

 synfolders.bat

@echo on

rem Save the current dir, [d] is disk letter, [p] is path name
pushd %~dp0

rem Change into my work dir
e:
cd \My Documents\My Company\ÈÕ¿¯\Code\pub

rem Coping my files to the public server
xcopy * l:\pub /s/e/y

rem Create the new dir with current system date in the pravite dir
rem [%DATE:~-10%] is get the last 10 ascii from the system date
md l:\zhangy\pub\%DATE:~-10%

rem Coping my file to the new dir in the pravite dir
xcopy * l:\zhangy\pub\%DATE:~-10% /s/e/y
xcopy * l:\zhangy\pub\whole /s/e/y

rem Resume the old dir
popd

@echo off 

Posted by Alex at 7:23 PM | Comments (0) | Edit | Taged: batch (3), DOS (2)

batch of operation under DOS

How to get a list of files name without path, size and datetime.rem '/b' is short...

How to get a list of files name without path, size and datetime.

rem '/b' is short list, '/on' is sort by name, '/a-d' is list all except dir.
dir /b/on/a-d/s > list.txt

How to add string to each of a batch of files name. 

rem add double '@' into the start of each file name with full path and extension.
FOR /R %I IN (*.fnc *.prc *.sql *.bdy *.spc) DO echo @@%~fnxI

for command reference

dos> for /? (part of)

 %~I         - expands %I removing any surrounding quotes (")
 %~fI        - expands %I to a fully qualified path name
 %~dI        - expands %I to a drive letter only
 %~pI        - expands %I to a path only
 %~nI        - expands %I to a file name only
 %~xI        - expands %I to a file extension only

How to rename a bench of files name

FOR /R %I IN (*.*) DO ren *.* *.bak

 

Posted by Alex at 7:01 PM | Comments (0) | Edit | Taged: batch (3), DOS (2)

Execute batch file before windows shutdown

click start -> run.enter gpedit.msc, then click OK.find the path of  Computer Configuration -> Windows Settings...

click start -> run.

enter gpedit.msc, then click OK.

find the path of 

Computer Configuration -> Windows Settings -> Scripts(Startup/Shutdown)

then add the path of batch files which you want to be executed before shutdown to the Shutdown item. 

Reference 

 

Posted by Alex at 3:27 PM | Comments (0) | Edit | Taged: batch (3)

translate Latin-1 in UTF-8 back to ASCII

Latin-1 characters (0x80 - 0xFF) are encoded as two-byte by UTF-8. UTF-8  ASCII HEX  BIN  DEC ...

Latin-1 characters (0x80 - 0xFF) are encoded as two-byte by UTF-8.

UTF-8  ASCII
HEX  BIN  DEC  DEC 
C2 A0  1100-0010 1010-0000 194 160  160 
C3 80
1100-0011 1000-0000 195 128 192 
C3 81  1100-0011 1000-0001 195 129 193

In some case, we might need to translate the Latin-1 characters encoded by UTF-8 back to one-byte.

First we need to know how UTF-8 encodes the Latin-1 from one-byte to two-byte. All two-byte UTF-8 characters have the following fixed encoding format. the value of 'x' depends the character being encoded.

110x,xxxx 10xx,xxxx 

In the first byte, the first two '11' means that this is a two byte character. the closest followed '0' is a fixed flag for spliting the first two flag bits with the rest bits.

In the second byte, the first '10' is fixed flag to make a difference with the leading byte, for example, 110x,xxxx is a leading byte for a character. All ASCII characters is leading by a 0xxx,xxxx, so if the application reads a byte like 10xx,xxxx and can not read the byte before it, then it can abandon this byte to read next.

For an ASCII encoded Latin-1 character 0xC1, its binary encoding is:

11000001

Fowlling shows how it maps to the two-byte in UTF-8 encoding.

11000011 10000001 

110 and 10 is fix flag.

000 is the fillers.

Method 1 

Following is the implement by DB2 SQL PL.

SET h_s = SUBSTR( str, i, 1 ) ;
SET l_s = SUBSTR( str, i + 1, 1 ) ;

SET h_s = CHR( MOD( ASCII( h_s ) * 64, 256 ) ) ;
SET l_s = CHR( MOD( ASCII( l_s ) * 4, 256 ) / 4 ) ;

RETURN CHR( ASCII( h_s ) + ASCII( l_s ) ) ;

In the code above, we use the operators *(multiple), /(divide), +(plus) and 'mod' instead of the bit-operators in C language, <<, >> and 'or'. It's the same with following C code.

h_s = h_s << 6 ;
l_s = l_s << 2 ;
l_s = l_s >> 2 ;

return h_s or l_s ;

Method 2

SET h_s = SUBSTR( str, i, 1 ) ;
SET l_s = SUBSTR( str, i+1, 1 ) ;

ELSEIF (h_s = 194) THEN -- latin-1
-- 0xC2 0x80 - 0xC2 0xBF ==> 0x80 - 0xBF
RETURN l_s ;

ELSEIF (h_s = 195) THEN -- latin-1
-- 0xC3 0x80 - 0xC3 0xBF ==> 0xC0 - 0xFF, 0xC0 - 0x80 = 0x40(64)
RETURN CHR( ASCII( l_s ) + 64 ) ;

END IF;

Posted by Alex at 9:48 PM | Comments (0) | Edit | Taged: DB2 Development (24), latin-1 (2), utf-8 (3)

A summary for UTF-8 and Unicode FAQ

ASCII and ISO-8859-1  There're 2 Character Sets most widely-used for computers. ASCII (Basic Latin) ISO-8859-1 (Latin-1)...

ASCII and ISO-8859-1 

There're 2 Character Sets most widely-used for computers.

  • ASCII (Basic Latin)
  • ISO-8859-1 (Latin-1)

The table in below shows the internal code range in computer for the 2 Character Sets.

Charset  Hex
Dec  Bin 
ASCII (Basic Latin) 0x00 - 0x7F  0 - 127  00000000 - 01111111 
ISO-8859-1 (Latin-1) 0x80 - 0xFF  128 - 255  10000000 - 11111111 

All ASCII and Latin-1 characters are encoded in one byte(8-bit).

People also refers ISO-8859-1 as the superset of ASCII. there's a little mess. 

ISO-10646 and Unicode 

In around 1991, ISO-10646 and Unicode merged their work, and will keep the 2 Character Sets consistent in the future.

Commonly, people say "unicode" when they are refering to encoding method, it's truly meaning UTF-16.

UTF-7
relatively unpopular 7-bit encoding, often considered obsolete
UTF-8
8-bit, variable-width encoding
UCS-2 and UTF-16 16-bit, fixed-width encoding, difference is that UCS-2 only supports the BMP
UCS-4 and UTF-32
functionally identical 32-bit fixed-width encodings
UTF-EBCDIC
unpopular encoding intended for EBCDIC based mainframe systems

Now Unicode 2.0 is widely used. 

Reference

 

Posted by Alex at 5:47 PM | Edit | Taged: ascii (1), iso-88591 (1), latin-1 (2), unicode (3), utf-8 (3)

operators in C Language

bit operators Example for bit operators且奇偶位已经被置位。    C语言运算符http://www.ddvip.net/program/c/index1/10.htm...

bit operators 

Example for bit operators

且奇偶位已经被置位。

 

 

Posted by Alex at 4:15 PM | Comments (0) | Edit | Taged: C (1)

special characters in programming

  space or blank -  minus sign  " quotation mark or double quote or double...
 
space
or blank
minus sign 
"

quotation mark
or double quote
or double quotation mark 

period
percent  /
slash
ampersand
:
colon

apostrophe
or single

semicolon
left parenthesis  less than
right parenthesis  equals
asterisk
>
greater than
plus sign
?
question mark
comma

underline
or underscore

vertical bar
^
caret
!
exclamation mark
[
left bracket
left brace
]
right bracket
right brace
   

Posted by Alex at 4:50 PM | Comments (0) | Edit | Taged: English (1), 特殊符号 (1)

get all Syntax Highlighting files for Ultraedit

open a dos window, then enter dos> ftp ultraedit.comUser (ultraedit.com:(none)): anonymous230 Anonymous user logged inftp> promptInteractive mode...

open a dos window, then enter 

dos> ftp ultraedit.com
User (ultraedit.com:(none)): anonymous
230 Anonymous user logged in
ftp> prompt
Interactive mode Off .
ftp> mget *

use prompt Off to prevent confirming for each file download.

Posted by Alex at 3:48 PM | Comments (0) | Edit | Taged: Highlighting (1), UltraEdit (3)

软件测试的术语

Unit test: 單元測試,針對某個component或method做的測試 Black box test: 黑箱測試,針對功能面來做測試 White box test: 白箱測試,針對內部實作的流程來做測試 Stress test: 壓力測試,測試系統的效能極限 Regression...

Unit test: 單元測試,針對某個component或method做的測試
Black box test: 黑箱測試,針對功能面來做測試
White box test: 白箱測試,針對內部實作的流程來做測試
Stress test: 壓力測試,測試系統的效能極限
Regression test: 回歸測試,當新功能增加的同時,會不會影響到舊功能的正確性
Integration test: 整合測試,兩個系統整合後有沒有錯誤
System test: 整個system跑起來後的測試
Alpha test: 開放內部測試人員做測試
Beta test: 開放給外部使用者做測試
Monkey test: 搞怪測試,盡可能惡搞來看系統穩定度
Acceptance test: 測試系統或產品是否會滿足客戶的需求

IBM 的一些测试team: 

FVT: funcational verification test
PVT: product verification test
SVT: system verification test
SIT: system integration test
UAT: user acceptance test

Posted by Alex at 5:37 PM | Comments (0) | Edit | Taged: 术语 (2), 测试 (1)

书籍推荐:《超越混沌,有效管理软件开发项目》

这是一本有关软件开发项目管理的书,虽然我不是一个项目经理,但我的工作需要与他们打交道,因此读读也未必是坏事。整本书近400页,但快速读一遍花费两三个晚上就足够了。 与其说这是一本书,不如说这是一本文集。这本书的作者达到了30位,他们多是工作在一线的项目管理者、咨询师,针对项目管理的不同方面阐述了自己独特的看 法,包括一些实用的技巧。如果你是一位项目管理者,你会发现,自己曾经遇到过的很多问题,在这里已经被很多人深刻的思考过,翻开看看,或许会有些有益的启 示。 本书论述的内容非常广泛,不仅有在大多数书籍上所能见到的一些“大道理”,也能找到一些细节问题的论述,比如项目中最复杂的人的管理,有关加班的讨论,转包项目中的陷阱,在公司内部维护一个可重用类库所面临的现实问题等等。 本书的详细信息如下: 《超越混沌,有效管理软件开发项目》 《Beyond Chaos, The Expert Edge in Manageing Software Development》...
这是一本有关软件开发项目管理的书,虽然我不是一个项目经理,但我的工作需要与他们打交道,因此读读也未必是坏事。整本书近400页,但快速读一遍花费两三个晚上就足够了。

与其说这是一本书,不如说这是一本文集。这本书的作者达到了30位,他们多是工作在一线的项目管理者、咨询师,针对项目管理的不同方面阐述了自己独特的看 法,包括一些实用的技巧。如果你是一位项目管理者,你会发现,自己曾经遇到过的很多问题,在这里已经被很多人深刻的思考过,翻开看看,或许会有些有益的启 示。

本书论述的内容非常广泛,不仅有在大多数书籍上所能见到的一些“大道理”,也能找到一些细节问题的论述,比如项目中最复杂的人的管理,有关加班的讨论,转包项目中的陷阱,在公司内部维护一个可重用类库所面临的现实问题等等。

本书的详细信息如下:

《超越混沌,有效管理软件开发项目》
《Beyond Chaos, The Expert Edge in Manageing Software Development》
(美)拉里.康斯坦丁 编
雷明 等译
电子工业出版社出版

Posted by Alex at 9:44 AM | Comments (0) | Edit | Taged: (4), 推荐 (2)

Data Mining-数据挖掘读书笔记(一)

正文 第一章从庞大的数据中提取出对数据库拥有者有价值的信息。跨学科:统计学、数据库技术、机器学习、模式识别、人工智能、可视化技术公用微观数据样本 public use microdata sample PUMS数据挖掘活动的类型: 探索性数据分析 Exploratory Data Analysis, EDA对数据进行探索,对于要寻找什么并没有明确的想法。 描述建模 Descriptive Modeling描述数据的所有特征,将数据区隔、分组、聚类。 预测建模...

正文

第一章

从庞大的数据中提取出对数据库拥有者有价值的信息。
跨学科:统计学、数据库技术、机器学习、模式识别、人工智能、可视化技术
公用微观数据样本 public use microdata sample PUMS

数据挖掘活动的类型:

  1. 探索性数据分析 Exploratory Data Analysis, EDA
    对数据进行探索,对于要寻找什么并没有明确的想法。
  2. 描述建模 Descriptive Modeling
    描述数据的所有特征,将数据区隔、分组、聚类。
  3. 预测建模 Predictive Modeling
    分类和回归法。
    建立一个模型,允许我们根据一个已知的变量来预测其他某个变量值。
  4. 寻找模式和规则
    上面的三类都致力于建模,这个类型是致力于模式探测。
    比如欺诈探测,频繁出现的商品组合等。
    可以采用基于关联规则(associaion rule)的算法。
  5. 根据内容检索
    用户有一种感兴趣的模式,并希望在数据库中找到相似的模式。
    Google系统采用了称为“PageRank”的数学方法来基于连接模式估计各个网页的相对重要性。
    还比如基于图象颜色、纹理和相对位置信息这样的内容描述提出查询。


评分函数:用于对给定模型或模型所选择参数的效果进行量化,以评价一个模型。广泛使用以下几种:
似然 likelihood
误差平方和
错误分类率

当模型已经确定,往往还需要发现模型中的最佳参数值,以使评分函数达到最大值或最小值(依赖于具体情况),这个任务称为优化(或
估计)问题。

数据挖掘的称谓:
数据挖掘,打捞(dredging),探察(snooping),垂钓(fishing)。

第二章

数据挖掘的有效性与原始数据的质量密不可分。GIGO (Garbage In, Garbage Out),非常形象。
数据质量分为两个方面:个别字段和记录的质量;数据集合的总体质量。

第四章

描述不确定性(uncertainty)的词汇:

  • 概率 probability
  • 偶然性 chance
  • 随机性 randomness
  • 运气 luck
  • 意外 hazard
  • 天数 fate

随想: 数据挖掘 - 不确定性建模 - 根据已有数据对未来事件预测 - 根据已有知识作出判断(诊断)- 专家系统 - 人工智能

频率论观点 frequentist view
主观概率观点认为,概率是一个人对一个特定事件能否发生的确信程度。因此概率不是外部世界的客观属性,而是个人的一种内心状态——因此可能由于个体的不同而不同。

从主观概率观点(subjective probability)派生出的数据分析理论和方法经常被称为贝叶斯统计(Bayesian statistics)。

第五章

数据挖掘算法是一个定义完备的(well-defined)过程,它以数据作为输入并产生模型或模型形式的输出。

定义完备制的是这个过程可以被精确的编码为有限的规则。

数据挖掘算法概览:

  1. CART(Classification and regression Trees)
  2. 反向传播(Backpropagation)
  3. A Priori


 

参考资源

  • 《数据挖掘原理》-《Principles of Data Mining》 By David Hand, Heikki Mannila and Padhraic Smyth

  • 《数据挖掘实践》- 《DataMining Cookbook》By Olivia Parr Rud

Posted by Alex at 10:39 AM | Comments (0) | Edit | Taged: Data Minning (1), 读书笔记 (1)

Oracle 学习笔记

2004-06-28lsnrctl start LISTENER    手工启动监听器(Listener)Oracle安装的默认HTTP 服务器可以通过以下 URL 进行访问:http://localhost:7778https://localhost:444318:27 2004-08-14用 select userenv('LANGUAGE') from dual;得到客户端环境的字符集 14:32 2004-9-2A表和B表是N..N的关系,中间有一个关系表R, A表中的部分数据有可能不在R表和B表中有对应的记录, 现在需要把A表中的数据取出,如果有的话,同时包含B和R的数据,否则B和R的列置为空.select...
2004-06-28

lsnrctl start LISTENER    手工启动监听器(Listener)

Oracle安装的默认HTTP 服务器可以通过以下 URL 进行访问:
http://localhost:7778
https://localhost:4443

18:27 2004-08-14
用 select userenv('LANGUAGE') from dual;得到客户端环境的字符集

14:32 2004-9-2
A表和B表是N..N的关系,中间有一个关系表R, A表中的部分数据有可能不在R表和B表中有对应的记录, 现在需要把A表中的数据取出,如果有的话,同时包含B和R的数据,否则B和R的列置为空.

select *
  from a
  left join r
    on a.id = r.id_a
  left join b
    on b.id = r.id_b

注意: sql中join各表的顺序很重要,当使用基本表编写sql时,一般很少犯错误,因为顺序搞错很可能编译不通过,但在使用拉平的视图编写sql时就可能导致潜在的问题,比如可能把join r和b的顺序弄反.

select *
  from a
  left join b
    on ...
  left join r
    on r.id_a = a.id
   and r.id_b = b.id

这样编写,对于r表中没有对应记录的数据就会产生笛卡儿乘积.


11:22 2004-9-28
取得一个给定日期的星期数,并使用指定的格式返回:
to_char( sysdate, 'day', 'nls_date_language=American' )

若需要锁定记录可是用:
select * from table for update 语句,当前session会获得一个锁,在提交之前或者回滚之前,其他session不能修改此表.


14:27 2004-9-29
用同义词方法,使用户可以像另一个对象的拥有者一样操作这个对象,不用加用户名前缀 user.table1

SQL>CREATE PUBLIC SYNONYM user1.DEPT FOR SCOTT.DEPT;
SQL>connect user1/pass1;
SQL>select * from dept; <- 引用 scott.dept 表

能不能更进一步?>

可以

<我的表很多,每个表都要建一次同义词?>

没有必要

<能不能对用户,而不是具体的表,建立同义词?>
不行

更简单的办法:

用户B登录,先执行:
alter session set current_schema=a
select * from table1 就是 select * from a.table1


15:25 2004-9-29
Oracle 8i 文档中对临时表的描述:
 Temporary Tables

In addition to permanent tables, Oracle can create temporary tables to hold session-private data that exists only for the duration of a transaction or session.

The CREATE GLOBAL TEMPORARY TABLE command creates a temporary table which can be transaction specific or session specific. For transaction-specific temporary tables, data exists for the duration of the transaction while for session-specific temporary tables, data exists for the duration of the session. Data in a temporary table is private to the session. Each session can only see and modify its own data. DML locks are not acquired on the data of the temporary tables. The LOCK command has no effect on a temporary table as each session has its own private data.

A TRUNCATE statement issued on a session-specific temporary table truncates data in its own session; it does not truncate the data of other sessions that are using the same table.

DML statements on temporary tables do not generate redo logs for the data changes. However, undo logs for the data and redo logs for the undo logs are generated. Data from the temporary table is automatically dropped in the case of session termination, either when the user logs off or when the session terminates abnormally such as during a session or instance crash.

You can create indexes for temporary tables using the CREATE INDEX command. Indexes created on temporary tables are also temporary and the data in the index has the same session or transaction scope as the data in the temporary table.

You can create views that access both temporary and permanent tables. You can also create triggers on temporary tables.

The EXPORT and IMPORT utilities can export and import the definition of a temporary table. However, no data rows are exported even if you use the ROWS option. Similarly, you can replicate the definition of a temporary table but you cannot replicate its data.



GLOBAL TEMPORARY关键字:

GLOBAL TEMPORARY    

specifies that the table is temporary and that its definition is visible to all sessions. The data in a temporary table is visible only to the session that inserts the data into the table.

A temporary table has a definition that persists the same as the definitions of regular tables, but it contains either session-specific or transaction-specific data. You specify whether the data is session- or transaction-specific with the ON COMMIT keywords (below).

For more information on temporary tables, please refer to Oracle8i Concepts.  

 
    

Restrictions:

    * Temporary tables cannot be partitioned, index-organized, or clustered.

    * You cannot specify any referential integrity (foreign key) constraints on temporary tables.

 

 
    

    * Temporary tables cannot contain columns of nested table or varray type.

    * You cannot specify the following clauses of the LOB_storage_clause: TABLESPACE, storage_clause, LOGGING or NOLOGGING, MONITORING or NOMONITORING, or LOB_index_clause.

 

 
    

    * Parallel DML and parallel queries are not supported for temporary tables. (Parallel hints are ignored. Specification of the parallel_clause returns an error.)

    * You cannot specify the segment_attributes_clause, nested_table_storage_clause, or parallel_clause.

    * Distributed transactions are not supported for temporary tables.

on commit 关键字:
ON COMMIT  
    

can be specified only if you are creating a temporary table. This clause specifies whether the data in the temporary table persists for the duration of a transaction or a session.  

 
    

DELETE ROWS  
    

specifies that the temporary table is transaction specific (this is the default). Oracle will truncate the table (delete all its rows) after each commit.  

 
    

PRESERVE ROWS  
    

specifies that the temporary table is session specific. Oracle will truncate the table (delete all its rows) when you terminate the session.


12:56 2004-10-8
plsql中可以使用for c in (select...) loop 语句,select后面可以跟一个或多个列名,并会把值自动付给变量c,变量c的类型为rowtype,
在循环体内可以通过如下方式引用:

for c in (select colno,cname from sys.col) loop
  dbms_output.put_line( c.colno || ': ' || c.cname );

end loop;


14:05 2004-10-13
oracle 存储过程中,当在返回类型的cursor中使用临时表,可以不用指定on commit关键字,外部程序通过ado访问都可以取到记录集。


17:15 2004-10-14
vc程序通过ado访问数据库,调用存储过程,如果在程序中使用了odbc数据源(dsn)则会有一些限制,比如:调用即含有cursor又含有返回值的存储过程会发生返回值无法取出。应直接使用connection对象获得数据库连接,可以避免此问题。


14:54 2004-10-15

to_char(sysdate, 'hh:mi:ss.ssss')
to_char(sysdate, 'hh:mi:ss.sssss')
是有效的,四个以下或者5个以上的's'都是非法的。

18:38 2004-10-18
可以在cursor的sql中使用order by子句,但如果sql当中含有 sequence的调用,则不允许使用order by子句。

21:24 2004-10-19
min()与max() 函数将会把重复的值合并掉,比如数据库中有多条记录col列的最小值为1,则min(col)会返回:1,与min(distinct col)返回结果相同.


17:02 2004-10-20
plsql 的记录集操作符:

UNION      All rows selected by either query.  
    两个合并重复记录的结果集,其交叉部分只出现一次.

UNION ALL      All rows selected by either query, including all duplicates.  
        两个未合并重复记录的结果集,不合并交叉的部分.(不合并任何部分)

INTERSECT      All distinct rows selected by both queries.  
        两个合并重复记录的结果集,只返回交叉部分.

MINUS      All distinct rows selected by the first query but not the second.  
    两个合并重复记录的结果集,从第一个结果集中去除交叉的部分.



select * from test t1 where b = (select max(b) from test t2 where t1.a = t2.a);
select a,max(b) from test t1 group by a;


16:14 2004-10-21
round(d, fmt) 函数返回一个日期以给定最小单位的最近时间.比如:
round(sysdate, 'dd') 返回下一日的日期
round(sysdate, 'yyyy') 返回下一年开始的日期
round(sysdate, 'mi') 返回下一分钟的时间

后面的fmt 可以为: yyyy, mm, dd, hh, mi, 不可以为秒ss.


oracle 中的时间分为12小时制和24小时制,当为二十四小时制时,00点00分为一天的起始。23点59分..为一天的结束,不允许出现24点的格式。
在12小时制当中,当然不允许出现大于12的小时数,比如13点。同时也不允许出现00点。24小时制中00点的时间被换算为12点,同时标注上午。即12小时制当中,上午12点00分是一天的开始,下午12点59分...是一天的结束。


14:47 2004-10-25
一个查询统计sql语句。表test中有,a,b,c三列,a为主键列,b为区分1,c为区分2,根据b,c建立一个二维表,各个块上的值为满足此两种区分条件的记录数目。
select t1.c,
       count(t2.a) n1,
       count(t3.a) n2
  from test t1
  left join test t2
    on t2.a = t1.a
   and t2.b = 1
  left join test t3
    on t3.a = t1.a
   and t3.b = 2
 group by (t1.c)


在存储过程中使用select 语句从临时表(commit on delete)中向应用程序返回cursor前,不能在存储过程内部使用commit语句清除临时表的数据,否则,应用程序得不到有效的cursor。


15:38 2004-10-26
使用 select ... for update 进行排他。

select ... for update;

update ...

commit;


20:26 2004-11-1
select id, num from t1 group by rollup(id);
可以在最后一行生成一个num的合计。

17:10 2004-11-2
如果采用 select id, count(decode(c1,'a', 1), count(decode(c2, 'b', 1), sum(1) from t1 group be rollup(id);
当decode中的等式右边的值都互斥时,sum(1)可以起到对列进行汇总的作用.
count(decode(c1,'a', 1) + count(decode(c2, 'b', 1) 最终会等于sum(1)

原理就是count(1)只对非空列进行计数,而sum(1)对每一列都产生了一个计数,只要第一个count与第二个count之间不产生重复计数,则上述等式成立.
如果把sum(1)变为sum(2)或者sum(0.5),则还会可以使总计的值是实际值的某个倍数.

但这样的语句就不会成立:
select dummy,1,2,3,4,sum(1) from dual group by rollup(dummy);

DUMMY          1          2          3          4     SUM(1)
----- ---------- ---------- ---------- ---------- ----------
X              1          2          3          4          1
               1          2          3          4          1


14:49 2004-11-5
在plsql的trigger中,可以使用when子句指定在何种条件下才触发trigger,但必须用在行级别的trigger(使用for each row),不能用在表级别的trigger.
而且不能够在when子句中调用用户自定义的函数. 在when子句中引用new 或者old不需要加":"前缀.

The expression in a WHEN clause must be a SQL expression, and it cannot include a subquery. You cannot use a PL/SQL expression (including user-defined functions) in the WHEN clause.


使用语句判断当前触发trigger的是哪类事件:
IF DELETING THEN
IF UPDATING THEN
IF INSERTING THEN

或者IF UPDATING( 'mycol')来判断是否更新了某列。
或者: after update of mycol on table

如果一个表名是new或者old,则可以在定义trigger时使用以下子句,避免冲突
BEFORE UPDATE ON new
REFERENCING new AS Newest

在代码中可以使用:Newest引用表new.


19:01 2004-11-10
使用utl_file包写入超过1024个字符数据时,需要在fopen方法中增加参数声明最大的size,如允许最大5000个字符:
utl_file.fopen( 'd:', 'ora.log', 'a', 5000 );

Posted by Alex at 2:17 PM | Comments (0) | Edit | Taged: Oracle (2), 学习笔记 (14)

J2ME 开发学习笔记

曾经参加过一次J2ME项目的开发,把学习到的一点点心得记在这里,以利查阅。 验证一个class文件时遇到错误:Class loading error: Illegal constant pool index JDK版本:1.4.2MIDP版本:1.0.3解决方法:在编译class时,加入参数:-target 1.1 如果描述文件或清单文件之中出现了非ASCII编码定义的文字,就一定要以ASCII编码的Unicode 形式出现才可以(不是UTF8),否則会出现问题。Jar内部要有一个清单文件(Manifest.mf),外部要有一个描述文件(.jad),其MIME类型为"text/vnd.sub.j2me.app-descriptor"。清单文件至少包含以下属性项:MIDlet-NameMIDlet-VersionMIDlet-VendorMIDlet-<n> (每一個屬於此MIDlet Suite 的MIDlet 都該有一個)MicroEdition-ProfileMicroEdition-Configuration 描述文件至少包含一下属性项:(所有属性必须以MIDlet开头)MIDlet-NameMIDlet-VersionMIDlet-VendorMIDlet-Jar-URLMIDlet-JAR-Size...

曾经参加过一次J2ME项目的开发,把学习到的一点点心得记在这里,以利查阅。

验证一个class文件时遇到错误:

Class loading error: Illegal constant pool index

JDK版本:1.4.2
MIDP版本:1.0.3
解决方法:在编译class时,加入参数:-target 1.1 

如果描述文件或清单文件之中出现了非ASCII编码定义的文字,就一定要以ASCII编码的Unicode 形式出现才可以(不是UTF8),否則会出现问题。

Jar内部要有一个清单文件(Manifest.mf),外部要有一个描述文件(.jad),其MIME类型为"text/vnd.sub.j2me.app-descriptor"。
清单文件至少包含以下属性项:

MIDlet-Name
MIDlet-Version
MIDlet-Vendor
MIDlet-<n> (每一個屬於此MIDlet Suite 的MIDlet 都該有一個)
MicroEdition-Profile
MicroEdition-Configuration

描述文件至少包含一下属性项:(所有属性必须以MIDlet开头)

MIDlet-Name
MIDlet-Version
MIDlet-Vendor
MIDlet-Jar-URL
MIDlet-JAR-Size

如果以上两个文件中属性值发生冲突,以描述文件(.jad)为主。

javax.microedition.midlet.MIDlet 类中定义了三个抽象方法:

startApp()
pauseApp()
destroyApp()

我们编写的任何手机MIDlet必须继承自这个类,并且需要实现这三个方法。

任何从paused状态变为activity状态,都要调用startApp()方法,因此,只需要被初始化一次的参数不应该写在startApp()方法中,应写在这个类的构建器(construector)方法中。

在MIDlet中不应该有public static void main(String[] args)方法,即使有,JAM也会忽略。

Java的命令行编译命令支持自动联编。比如A类中引用了B类,编译A时,编译器会自动编译B。前提是B类要在当前的classpath路径中,并且以B.java命名。
MIDP中的验证工具Preverify.exe具有以上类似的功能。

MIDlet 自身没有改变自己状态的能力,需要调用相关方法,由JAM代劳。需要注意的是:上述提到的三个方法,并不能控制MIDlet生命周期,MIDlet自身调用这些方法,仅仅是执行了这些方法中的代码而已,并没有改变MIDlet自身的状态。必须额外调用其他三个方法:

notifyPaused() //change to pause state
resumeRequest() //change to activity state
notifyDestroyed() //destroy MIDlet

一个最简的MIDlet程序如下:

//------------------------------------------------------
import javax.microedition.midlet.*;
publlic class HelloMIDlet extends MIDlet
{
public HelloMIDlet()
{
// 构建器
}
public void startApp()
{
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
//------------------------------------------------------

启动应用的一般步骤:
startApp() 
-> 进入运行状态 
-> 取得代表屏幕的Display对象: Display.getDisplay(this)
-> 设置显示画面: Display.setCurrent(Displayable 类的实例)

一个可以显示画面的最简MIDlet程序如下:

//------------------------------------------------------
import javax.microedition.midlet.*;
publlic class HelloMIDlet extends MIDlet
{
private Display display;
public HelloMIDlet()
{
//... ...
display = Display.getDisplay(this);
//... ...
}
public void startApp()
{
//... ...
TextBox t = new TextBox(... ...);
display.setCurrent(t);
//... ...
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
//------------------------------------------------------

MIDlet的事件处理,共有四类

  1. 高级事件处理: 由程序触发
  2. 低级事件处理: 当手机按钮或触摸屏被触摸时触发
  3. 绘图事件处理
  4. Display类

MIDP1.0中,所有可视类都是由Displayable这个抽象类派生。由Displayable 派生出了Screen类和Canvas类。
Screen类主要用来处理高级事件,Canvas类用来处理低级事件。

高级事件处理实际上是向屏幕上的可视类注册了一组选项和对应的处理方法,可视类捕获到相应的事件便会触发由你自己实现的事件处理方法。
高级事件处理简单,但不够灵活。

低级事件处理的一些比较重要的事件:

KeyPressed()
KeyReleased()
KeyRepeated() 按住某个键不放产生连续事件,只有部分手机支持,使用hasRepeatEvents()判断。

在MIDP1.0 中定义了一些常数:KEY_NUM0 ~ KEY_NUM9, KEY_STAR, KEY_POUND。分别对应0 - 9数字键,*和#键。
其它与游戏键盘代码相关的常数:UP, DOWN, LEFT, RIGHT, FIRE, GAME_A ~ GAME_D。使用Canvas提供的方法getGameAction()与getKeyCode(),在键盘代码与KeyCode之间进行提取。

pointerPressed() 传入触摸笔点选位置的坐标
pointerReleased() 传入触摸笔离开位置的坐标
pointerDragged() 传入触摸笔拖拽时所在位置的坐标
hasPointerEvents() 判断手机是否支持触摸笔事件
hasPointerMotionEvents() 判断是否支持拖拽事件

 

Posted by Alex at 11:45 AM | Comments (0) | Edit | Taged: J2ME (1), 学习笔记 (14)

Python学习笔记

22:22 2004-3-29Python使用缩进来控制程序块,可以使用空格或制表符缩进,缩进多少不限制,但上下文要保持一致。注意:不能混合使用空格与制表符。Python的基本变量属于弱类型,比如你甚至可以把一个字符串赋给一个变量,然后再把一个整形值赋给它,它的变量类型自动进行了转换。Python内置一个内存回收管理的机制。Python有类的概念,可以方便的进行面向对象的编程。Python与其他许多语言一样,也有自己的库,称为“模块”,使用前需要使用import 语句导入。你也可以定制自己的模块,然后像导入系统内置模块那样导入它。Python中支持列表、元组、数组、集合等数据类型。Python中连接两个字符串使用"+"运算符。你甚至可以连接两个列表数据类型。Python中支持运算符重载。在UNIX下编写Python程序,需要在第一行写入:#! /usr/bin/python用于标明解释器的位置,这与shell很相似。Python中没有内置的堆栈或队列数据类型,可以使用列表数据类型自己实现。在Python中退出程序使用:sys.exit();需要导入sys模块。16:52 2004-4-18...
22:22 2004-3-29

Python使用缩进来控制程序块,可以使用空格或制表符缩进,缩进多少不限制,但上下文要保持一致。注意:不能混合使用空格与制表符。

Python的基本变量属于弱类型,比如你甚至可以把一个字符串赋给一个变量,然后再把一个整形值赋给它,它的变量类型自动进行了转换。

Python内置一个内存回收管理的机制。

Python有类的概念,可以方便的进行面向对象的编程。

Python与其他许多语言一样,也有自己的库,称为“模块”,使用前需要使用import 语句导入。你也可以定制自己的模块,然后像导入系统内置模块那样导入它。

Python中支持列表、元组、数组、集合等数据类型。

Python中连接两个字符串使用"+"运算符。你甚至可以连接两个列表数据类型。

Python中支持运算符重载。

在UNIX下编写Python程序,需要在第一行写入:
#! /usr/bin/python
用于标明解释器的位置,这与shell很相似。

Python中没有内置的堆栈或队列数据类型,可以使用列表数据类型自己实现。

在Python中退出程序使用:
sys.exit();
需要导入sys模块。

16:52 2004-4-18

Posted by Alex at 10:22 PM | Comments (0) | Edit | Taged: Python (1), 学习笔记 (14)

Shell 编程学习笔记

在当前shell中而不是子Shell中执行cmd.sh程序: $ source cmd.sh 或者 $ . cmd.sh test 用于测试一个表达式的返回值: if test "$1" = "abc" ...

在当前shell中而不是子Shell中执行cmd.sh程序:

$ source cmd.sh 

或者

$ . cmd.sh

test 用于测试一个表达式的返回值:

if test "$1" = "abc" 
then
cmd
fi

可以用[]代替'test'关键字:

if [ "$1" = "abc" ]
then
cmd
fi

但[后和]前必须留有空格,等号两边也要留空格。
一些比较运算符:前面一列用于整形数据,后面一列用于字符串数据

-lt <
-gt >
-lq >=
-gq <=
-ne !=
-eq =

其他参数:

-n "str" 串是否不为空
-e "filename" 文件是否存在
-f "filename" 文件是否是普通文件
-d "filename" 是否是目录

shell后面的参数:

$# 参数的个数,不包括命令本身
$? 最后一个进程返回值
$0 命令本身
$1 第一个参数,依次类推:$2,$3...
$@ 所有的参数,形如:"$1" "$2" "$3" ...
$$ 包含当前进程ID
$! 最后一个后台进程的ID

可以用如下shell取出传入参数:

#!/bin/bash
if [ $# -lt 6 ]
then
echo usage: -a para1 
echo [-b para2] [-c para3]
exit 0
fi
for element in #@
do
if [ "$1" = "-a" ]
then
var1="$2"
elif [ "$1" = "-b" ]
then
var2="$2"
elif [ "$1" = "-c" ]
then
var3="$2"
fi
shift # 改变参数的引用,$2变成$1, $3变成$2 ...
done

注意:shift命令将会改变'$@'的值,循环结束后,'$@'值变为null。

有关awk的使用:

### m.tmp ###
table1
table2
...
#############

把m.tmp文件中的每一行前后插入字符串,并写回文件:

cat m.tmp | awk '{ print "delete from " $1 ";" }' > m.tmp

避免交互过程,一次性设定新密码:

echo "newpasswd" | passwd --stdin username

从命令行读入:

#!/bin/sh
while true
do
echo "are you sure to do this? [y/n]"
read str
if [ "$str" = "y" -o "$str" = "Y" ]
then
break
if [ "$str" = "n" -o "$str" = "N" ]
then
exit 0
else
continue
fi
done

read 后面可以不声明变量,则用户输入会写入默认变量$REPLY.

 

Posted by Alex at 11:43 AM | Comments (0) | Edit | Taged: Shell (8), 学习笔记 (14)

应用Rational Rose 进行状态机分析与设计实例

状态机用于对系统中的动态行为建模,一般使用一个状态机对一个对象经历的状态变化序列进行建模,状态是指对象在生命周期中的一个条件或状况,把这个状态机用UML图的形式表现出来,就是一个状态图。一个系统中会有很多对象,一般仅对状态序列复杂的对象画出状态图,从而进行细致的分析。 前言 本文结合一个具体的实例,试图通过对一个对象状态机的分析,画出一个完整的状态图。笔者初学UML,大胆下笔此文,难免纰漏,以期能抛砖引玉,请您不吝批评指正。 状态机用于对系统中的动态行为建模,一般使用一个状态机对一个对象经历的状态变化序列进行建模,状态是指对象在生命周期中的一个条件或状况,把这个状态机用UML图的形式表现出来,就是一个状态图。 一个系统中会有很多对象,一般仅对状态序列复杂的对象画出状态图,从而进行细致的分析。   用户的需求 首先介绍一下我们要实现系统的需求,本系统是一个专家网络,主要包括三种资源。 第一,网络服务的提供者。与专家合作,签署协议。 第二,领域专家,通过专家网络进行专业问题解答,并可以通过网络服务的提供者收取提问者的佣金。 第三,专家网络的最终用户,即提问者,可以通过专家网络提出问题,并选择相应的领域专家进行回答。 此系统中的一个核心对象就是问题(Questions),我们要针对它的状态机进行分析,给出状态图。 一个问题将籍由系统中不同角色的行为而相应改变自己的状态。 起初一个问题由提问者添加到系统中,编辑完成后,选择一个专家并提交,相应的专家在登录后可以查看到此问题,此时,提问者不能够再对问题进行修改。 一个问题在未提交给任何专家之前,提问者可以删除此问题。但一旦提交或者已经回答,就无法删除。 专家可以选择接受或者拒绝,如果专家拒绝此问题,则问题会重新发回给提问者,其可以在此编辑此问题,并选择不同的专家进行提问。当选择编辑此问题,则提问者可以在自己的视图看到问题已经被接受,并正在进行回答,但提问者看不到正在编辑的问题解答内容。...

状态机用于对系统中的动态行为建模,一般使用一个状态机对一个对象经历的状态变化序列进行建模,状态是指对象在生命周期中的一个条件或状况,把这个状态机用UML图的形式表现出来,就是一个状态图。一个系统中会有很多对象,一般仅对状态序列复杂的对象画出状态图,从而进行细致的分析。

前言

本文结合一个具体的实例,试图通过对一个对象状态机的分析,画出一个完整的状态图。笔者初学UML,大胆下笔此文,难免纰漏,以期能抛砖引玉,请您不吝批评指正。

状态机用于对系统中的动态行为建模,一般使用一个状态机对一个对象经历的状态变化序列进行建模,状态是指对象在生命周期中的一个条件或状况,把这个状态机用UML图的形式表现出来,就是一个状态图。

一个系统中会有很多对象,一般仅对状态序列复杂的对象画出状态图,从而进行细致的分析。

 

用户的需求

首先介绍一下我们要实现系统的需求,本系统是一个专家网络,主要包括三种资源。

第一,网络服务的提供者。与专家合作,签署协议。

第二,领域专家,通过专家网络进行专业问题解答,并可以通过网络服务的提供者收取提问者的佣金。

第三,专家网络的最终用户,即提问者,可以通过专家网络提出问题,并选择相应的领域专家进行回答。

此系统中的一个核心对象就是问题(Questions),我们要针对它的状态机进行分析,给出状态图。

一个问题将籍由系统中不同角色的行为而相应改变自己的状态。

起初一个问题由提问者添加到系统中,编辑完成后,选择一个专家并提交,相应的专家在登录后可以查看到此问题,此时,提问者不能够再对问题进行修改。

一个问题在未提交给任何专家之前,提问者可以删除此问题。但一旦提交或者已经回答,就无法删除。

专家可以选择接受或者拒绝,如果专家拒绝此问题,则问题会重新发回给提问者,其可以在此编辑此问题,并选择不同的专家进行提问。当选择编辑此问题,则提问者可以在自己的视图看到问题已经被接受,并正在进行回答,但提问者看不到正在编辑的问题解答内容。

专家解答完成之后,把问题提交给提问者,提交之后,专家无法再进行编辑。此时,提问者可以查看专家的解答内容,如果认可解答内容,可以选择接受,问题被归档。否则,可以提出拒绝解答内容,拒绝的问题由网络服务提供者进行处理。如果提问者在问题解答后的一个确定的时间内没有作出明确的接受或者拒绝的回复,则系统会自动认为问题已经解答完毕,并归档。

提问者在提出问题时,可以选择问题的解答级别,分为简单解答和详细解答,在选择专家时,可以查看专家对不同级别问题进行解答所需要花费的最长时间以及费用。当专家开始编辑此问题后,则计时开始,若没有在规定的时间内完成解答,则问题会过期,由网络服务提供者确认后重新发送给提问者。提问者可以重新进行编辑提交。

如果提问者第一次提问要求为简单解答,在专家回复后,提问者可以选择要求更为详细的回答,问题会被再次提交给同一个专家。

以上概要描述了主要的需求,通过需求我们需要分析出Question对象所要经历的状态序列,以及触发状态转换的事件和对象的动作。

 

分析状态机

当问题被提问者加入系统,但还未提交给专家前,问题处于编辑状态,我们称状态为"Uploaded"。

当提问者删除了一个问题,则问题处于无效状态,我们称状态为"Removed"。

当问题由提问者第一次提交给专家后,问题处于等待解答状态,我们称状态为"New"。

当专家拒绝此问题后,问题状态处于被拒绝状态,我们称状态为"Refused by expert"。

当专家开始编辑解答内容,表示问题已经被接受,正在进行编辑,我们称状态为"Work in progress"。

如果专家没有及时解答完问题,问题会在规定的时间内过期,我们称状态为"Expired"。

当专家解答完问题,提交给提问者之后,问题等待提问者进行Check,我们称状态为"Answered"。

如果提问者拒绝了专家的回答,则问题处于被拒绝状态,我们称状态为"Refused by Asker"。

如果提问者接受了专家的解答,则问题结束,我们称状态为"Finished"。

提问者还可以要求专家更详细的回答,提问者再次向同一个专家提交更详细的解答请求后,问题在此回到等待解答状态,与前面的"New"状态进行区分,我们称状态为"2th. New"。

此外还有两个特殊的状态,就是初态和终态。

以上我们根据需求列举出了问题对象在系统中经历的所有状态可能,以及出发状态转换的条件。接下来我们在Rational Rose中使用状态图画出状态的序列。

 

画出状态图

状态图如下:

 

参考资源

  • 下载本示例使用的的Rational Rose 建模文件,软件版本:Rational Rose 2002.05.00
  • 《UML 用户指南》《The Unified Modeling Language User Guide》Grady Booch, James Rumbaugh and Ivar Jacobson 合著,机械工业出版社出版。
  • 下载UML 电子教程 北京航空航天大学软件工程研究所撰文

Posted by Alex at 10:59 AM | Comments (0) | Edit | Taged: Rational Rose (1), 状态机 (1)

CMS-一篇关于分类资源管理系统设计思路的笔记

正文 在信息技术高度发达的今天,越来越多的人们意识到自己正面临着管理类型各异、并且日益增长的数字资源,当这些资源积累膨胀到一定程度之后,大概每个人都会问一个相同的问题,也是我一直在思考的一个问题--如何把自己所收集的大量资源很好的分类管理起来?这些资源的文件类型是多种多样的, 比如HTML, PDF, TXT, JPG, ZIP, EXE, TAR, GZ等等, 并且用途各异, 有各类电子书籍, 软件源码, 图片, 或许是你喜欢的文学作品和电影, 甚至是你一年以前的重要邮件,...

正文

在信息技术高度发达的今天,越来越多的人们意识到自己正面临着管理类型各异、并且日益增长的数字资源,当这些资源积累膨胀到一定程度之后,大概每个人都会问一个相同的问题,也是我一直在思考的一个问题--如何把自己所收集的大量资源很好的分类管理起来?

这些资源的文件类型是多种多样的, 比如HTML, PDF, TXT, JPG, ZIP, EXE, TAR, GZ等等, 并且用途各异, 有各类电子书籍, 软件源码, 图片, 或许是你喜欢的文学作品和电影, 甚至是你一年以前的重要邮件, 总之, 你很难用一种分类方法把它们组织得很好, 它们有些内容所具有的含义是多重的, 比如你的一张旅行照片, 可以把它放在顶级叫做图片的目录下--当然这下面可能还要复杂一点, 这样对于所有的图片来说, 这是清晰的. 但你大概也希望在阅读这次出行游记的时候也可以很方便的看到它, 这样我就有了把这张图片放在我的 文档->旅游->图片 目录下的需求, 保存两份就带来冗余的问题. 很明显, 传统文件系统的树形组织关系已经不能很好的满足你方便访问文件的需要. 你需要一种更好的文件访问方式, 即可以通过图片分类找到这张照片, 也可以在阅读你半年前写的一篇游记的同时看到它, 或许你还会期望有更多的途径. 于是你会想办法给这些资源加以分类, 并做出向上面那样的交叉链接, 即使是并不关心它们如何存储, 如何实现访问, 只要给出逻辑上的关系即可, 但你可能发现, 这也不是一件容易的事情. 

于是你就会期望一个内容管理系统,来帮助处理这一切事情,我的想法也是从这里开始的,并且尝试给出一个更清晰的定义。
首先, 我把它仅仅看作一个分类管理系统, 不负责真正数据的存储, 这应该由另一个系统来作, 比如(对象/关系)数据库, 文件系统等. 这两个系统之间应该可以很好的协作、沟通,但不应该耦合太紧,即需要一个清晰的接口。

其次, 我想这套分类管理系统应该提供这样一个功能, 一个小的文本处理程序可以即时的呼叫出来分析并处理当前浏览的文本--比如网页, 或者成批的分析处理一个目录, 或者是一篇word文档,把这些信息按我的要求提取出来并保存, 可以称之为"导入". 这个功能并不属于"分类管理系统"的核心, 它只是针对不同类型数据来源的一个导入插件而已. 在这个系统之上应该留出这样的接口,使得不同的人可以为不同的数据类型更方便的进入这套系统而开发插件。不过如果想要方便的实现数据提取,更多的依赖于源数据的良构性。

关于数据的分类, 将一个对象的属性抽象, 从它的各种属性中抽象出固有的属性, 以区别其它人为赋给的社会属性. 比如人的性别相对比较稳定, 就是固有属性, 其它比如头衔, 工作单位, 家庭住址等就是一些社会属性. 这本身就是一个可以讨论的足够深的话题。 

最底层的数据存储也应该是用户可控的, 比如你在WEB页面看到的一篇文章在后端可能相对于一个或多个文件, 并且这些文件的排列并不是无序的, 你应该可以从表示层推测出这些文件的存储结构, 并方便的进行访问. 这对于备份或是小范围操作是非常方便的, 尽管你还有这个系统提供的其他手段, 但这是你最基本的权利. 在这个系统的末端,我想应该提供一个基于WEB的数据表示层给使用者. 

现在可以把这套系统想像成一个桥, 一边是基本的数据, 一边是这些数据的受益群体. 这座桥根据基本的数据生成一个索引, 而在另一边, 根据数据的受益群体, 而提供相应的视图. 基本数据可以认为是基质的, 分类相对稳定的. 而视图则可以有多个, 视图也体现为某种分类, 但这种分类的依据是数据受益群体的使用习惯, 在某些情况下, 视图的分类方式可能与基本数据的分类方式一致. 

这样, 在基本数据的类别和用户视图的类别之间存在一个映射关系, 提供给用户的视图可能是所有基本数据的一个子集, 也可以是全部. 而这种映射关系就是这座桥, 确切的说是一组桥. 

对于基本数据, 可以是一个全新的数据集合, 也可以是一个现有的, 已经分类好的数据集合. 我们这座桥并不试图去改变原有的数据分类方式和分类标准, 首先, 根据现有的数据内容, 为数据受益群体提供一个合适的视图, 并在基本数据的分类和视图之间作一个映射关系. 

用户视图的建立和选择, 以及如何建立与基本数据的映射关系, 是由熟悉这些数据所涉及领域的专家来完成的, 这套系统并不能代替你完成. 好比我给你提供一个书架, 仅此而已, 如何整理你的书籍, 以及该放在书架的什么位置, 这是你的事. 当然, 我会让书架更舒适一些, 更方便你的阅读和取用. 

相对于原来的想法--这套系统管理所有的资源, 现在有些不同. 我们把它想像成专有的, 比如一个这样的系统只管理书籍资源, 而另一个这样的系统专门管理音像资源, 可能还有其他的专门管理用于计算机的原始代码, 而这些多个不同的、可相互集成的、可扩展的系统, 放在一起, 就实现了最初的管理所有资源的目标. 可以把对每一种资源的管理视为一个组件, 而我们可以很方便的追加一种新的组件来管理新增的资源种类. 这要求这些组件之间是可协作的, 并且要设计良好, 藕合太松, 达不到协作的目标; 藕合太紧, 失去组件的意义, 增加及删除一个组件都会变得很复杂. 

下面的内容会深入一些, 会设计到一些架构方面的考虑.

这套系统是工作在一个已有的, 分类好的数据集合基础之上的. 如果还没有, 我们也可以帮你建立一个. 我们把这个集合里面的数据仅仅理解为数据本身, 并不包含数据的其它附加属性, 即使在这个集合里面包含这些附加描述, 我们也不去理会. 因为这会引起我们的系统和已有数据集合的接口复杂化, 而这对于我们希望其工作在大多数现有数据集合上的愿望是相悖的. 

我们把这些基本数据的属性放在我们这套系统的内部存储, 这无论如何是必需的, 这是这套系统有价值以及可以工作的前提. 这会带来两方面的工作.

第一, 原有基本数据相关信息的导入问题, 这里面包含原有数据的存取位置, 存取方式, 相关属性等等. 倚赖于原始数据量, 这个工作量可能非常巨大, 会有一些必需的手动操作和人工干预, 大部分工作希望可以通过一个或多个导入脚本来完成,例如使用perl.

第二, 可能存在这样的情况, 原有的数据拥有使用者所倚赖的一些特有的属性, 而我们的系统中恰巧没有对这项属性的描述和处理. 那么我们就面临着在系统中增加这种处理的额外工作. 

对于第二个问题, 我们应尽量避免, 就是说我们在设计针对某种资源种类的组件时, 要进尽可能把用户的需求覆盖到, 不同的用户的通过配置来筛选这些功能. 在这样的情况下, 如果真的有一个特殊的用户需求, 我想用户也不会介意这导致的额外开发成本, 这是他应该承担的,相对于他可以从中获得的利益. 

另外, 我们是否可以继续抽象一下, 把基本的数据集合的适用范围再扩大一些, 超出数字资源的范畴, 比如一个真实的, 以纸质保存书籍的图书馆. 那么我们这套系统是否也应该适合它, 并很好的运作, 除了那些在线对基本数据本身的操作, 比如订正, 增加等. 但我们仍然可以对这些非电子资源的属性进行在线操作, 比如书名和借阅状态的查询, 办理借阅手续等. 而在这种情况下, 我们系统内部保存的这本书的位置应该是他的书架编号, 而不是一个电子链接. 
				  / 用户视图一	- - 用户群体一
电子书籍资源	电子书籍管理组件	- - 用户视图二	- - 用户群体二
			/\	  \ 用户视图三	- - 用户群体三
			//
			\/	  / 用户视图一	\
数字音像资源	数字音像管理组件	- - 用户视图二	- - 用户群体
			/\	  \ 用户视图三	/
			//
			\/
传统图书馆		传统书籍管理组件	- - 用户视图	- - 用户群体


	基本数据源				资源管理组件
-----------------------	--------------------------------------------------------
|		|	|	|	|	|	|	|
|	文件系统	|	| 导入	| XML	|	| 导入	| 用户	|
|	对象关系DB	|  <=>	| 脚本	| 或	| 逻辑层	| 插件	| 视图	|
|	LDAP数据源	|	|  =>	| 关系DB	|	|  <=	|	|
|	非数字资源	|	|	|	|	|	|	|
-----------------------	--------------------------------------------------------



最近由于项目的需要,去方正研究院看了一下方正博思3.0的功能演示。感觉博思的功能已经比较完善,已经可以管理很多种类型的数据资源。比如已经有单文 档,复合文档,复合资产等概念;基于图片的检索;功能强大的相关性浏览;支持数据采集,上载,打包,下载以及光盘小博思等概念;具有用户权限管理,数据加 密等功能。博思需要工作在一个已有大型数据库之上,所有管理的数据,包括大数据量的流媒体,均导入数据库以大对象方式存放,不以文件系统形式存在。即,还 没有明确的与现有分类资源进行搭接的接口。

 

参考资源

 

Posted by Alex at 10:33 AM | Comments (0) | Edit | Taged: cms (1)

在PL/SQL 开发中调试存储过程和函数的一般性方法

正文 Oracle 在PLSQL中提供的强大特性使得数据库开发人员可以在数据库端完成功能足够复杂的任务, 本文将结合Oracle提供的相关程序包(package)以及一个非常优秀的第三方开发工具来介绍在PLSQL中开发及调试存储过程的方法,当然也适用于函数。 本文所采用的软件版本和环境: 服务器:Oracle 8.1.2 for Solaris 8 开发工具:PL/SQL Developer 4.5 准备工作 在开始之前, 假设您已经安装好了Oracle的数据库服务,...

正文

Oracle 在PLSQL中提供的强大特性使得数据库开发人员可以在数据库端完成功能足够复杂的任务, 本文将结合Oracle提供的相关程序包(package)以及一个非常优秀的第三方开发工具来介绍在PLSQL中开发及调试存储过程的方法,当然也适用于函数。

本文所采用的软件版本和环境:

  • 服务器:Oracle 8.1.2 for Solaris 8
  • 开发工具:PL/SQL Developer 4.5

准备工作

在开始之前, 假设您已经安装好了Oracle的数据库服务, 并已经建立数据库, 设置好监听程序, 以允许客户端进行连接; 同时您已经拥有了一台设置好本地Net服务名的开发客户机, 并已经安装好PL/SQL Developer开发工具的以上版本或者更新.

在下面的示例代码中,我们使用Oracle数据库默认提供的示例表 scott.dept 和 scott.emp. 建表的语句如下:

create table SCOTT.DEPT
(
DEPTNO NUMBER(2) not null,
DNAME VARCHAR2(14),
LOC VARCHAR2(13)
)
create table SCOTT.EMP
(
EMPNO NUMBER(4) not null,
ENAME VARCHAR2(10),
JOB VARCHAR2(9),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7,2),
COMM NUMBER(7,2),
DEPTNO NUMBER(2)
)

从一个最简单的存储过程开始

我们现在需要编写一个存储过程, 输入一个部门的编号, 要求取得属于这个部门的所有员工信息, 包括员工编号和姓名. 员工的信息通过一个cursor返回给应用程序.

create or replace procedure usp_getEmpByDept(
in_deptNo in number,
out_curEmp out pkg_const.REF_CURSOR 
) as
begin
open curEmp for 
select empno,
ename
from scott.emp
where deptno = in_deptNo;
end usp_getEmpByDept;

上面我们定义了两个参数, 其中第二个参数需要利用cursor返回员工信息, PLSQL中提供了REF CURSOR的数据类型, 可以采用两种方式进行定义, 一种是强类型,一种是弱类型, 前者在定义时指定cursor返回的数据类型, 后者可以不指定, 由数据库根据查询语句进行动态绑定.

在使用前必须首先使用TYPE关键字进行定义, 我们把数据类型REF_CURSOR定义在自定义的程序包中: pkg_const

create or replace package pkg_const as
type REF_CURSOR is ref cursor;
end pkg_const;

注意: 这个包需要在创建上面的存储过程之前被编译, 因为存储过程用到了包中定义的数据类型.

调试存储过程

使用PL/SQL Developer 登录数据库, 用户名scott, 密码默认为: tiger. 将包和存储过程分别编译, 然后在左侧浏览器的procedure栏目下找到新建的存储过程, 点击右键, 选择"Test"/"测试", 在下面添好需要输入的参数值, 按快捷键F8直接运行存储过程, 执行完成之后, 可以点开返回参数旁边的按钮查看结果集.

如果存储过程内部语句较复杂, 可以按F9进入存储过程进行跟踪调试. PL/SQL Developer提供与通用开发工具类似的跟踪调试功能, 分为step、step over、step out 等多种方式, 对于变量也可进行trace或者手动赋值。

在存储过程中写日志文件

以上方法可以在开发阶段对编写和调试存储过程提供最大限度的方便,但为了在系统测试或者生产环境中确认我们的代码是否正常工作时,就需要记录log。

PLSQL提供了一个UTL_FILE包,通过定义UTL_FILE包中的FILE_TYPE类型,可以获得一个文件句柄,通过此句柄可以实现一般的文件操作功能。但默认的数据库参数是不允许使用UTL_FILE包的,需要手动进行配置,使用GUI的管理工具或者手工编辑 INIT.ORA文件,找到 "utl_file_dir" 参数,如果没有,则添加一行,修改成如下:

utl_file_dir='/usr/tmp'

或者

utl_file_dir=*

第一种方式限定了在UTL_FILE包中可以存取的目录,第二种方式则不进行限定。无论哪种方式,都要保证运行数据库实例的用户,一般是oracle,拥有此目录的存取权限,否则在使用包的过程中会报出错误信息。

注意等号左右不要留空格,可能会引起解析错误,导致设置无效。

下面在上面的存储过程中加入记录log的代码:

create or replace procedure usp_getEmpByDept(
in_deptNo in number,
out_curEmp out pkg_const.REF_CURSOR 
) as
fi utl_file.file_type;
begin
if( pkg_const.DEBUG ) then 
fi := utl_file.fopen( pkg_const.LOG_PATH, to_char( sysdate, 'yyyymmdd' ) || '.log', 'a' );
utl_file.put_line( fi, ' ****** calling usp_getEmpByDept begin at ' || to_char( sysdate, 'hh24:mi:ss mm-dd-yyyy' ) || ' ******' );
utl_file.put_line( fi, ' INPUT:' );
utl_file.put_line( fi, ' in_chID => ' || in_chID );
end if;
open curEmp for 
select empno,
ename
from scott.emp
where deptno = in_deptNo;
if( pkg_const.DEBUG ) then 
utl_file.put_line( fi, ' RETURN:' );
utl_file.put_line( fi, ' out_curEmp: unknown' );
utl_file.put_line( fi, ' ****** usp_getEmpByDept end at ' || to_char( sysdate, 'hh24:mi:ss mm-dd-yyyy' ) || ' ******' );
utl_file.new_line( fi, 1 );
utl_file.fflush( fi );
utl_file.fclose( fi );
end if;
exception
when others then
if( pkg_const.DEBUG ) then 
if( utl_file.is_open( fi )) then
utl_file.put_line( fi, ' ERROR:' );
utl_file.put_line( fi, ' sqlcode = ' || sqlcode );
utl_file.put_line( fi, ' sqlerrm = ' || sqlerrm );
utl_file.put_line( fi, ' ****** usp_getEmpByDept end at ' || to_char( sysdate, 'hh24:mi:ss mm-dd-yyyy' ) || ' ******' );
utl_file.new_line( fi, 1 );
utl_file.fflush( fi );
utl_file.fclose( fi );
end if;
end if;
/* Raise the exception for caller. */
raise_application_error( -20001, sqlcode || '|' || sqlerrm );
end usp_getEmpByDept;

在上面的代码中,我们又引用了两个新的常量:

DEBUG
LOG_PATH

分别定义了调试开关参数和文件路径参数,对此,我们需要修改我们前面定义的程序包:

create or replace package pkg_const as
type REF_CURSOR is ref cursor;
DEBUG constant boolean := true;
LOG_PATH constant varchar2(256) := '/usr/tmp/db';
end pkg_const;

在代码块的起始处,将输入参数的名称与值成对的记入log文件,在代码块的正常退出部分,将输出参数的名称和数值也成对的记录下来,如果程序非正常退出,则在exception 的处理部分,把错误代码及错误信息写入log文件。一般使用这些信息就可以较迅速的找出程序运行中出现的大部分错误。

注意:如果返回参数的类型是cursor,是无法在存储过程内部将返回的结果集一条一条写入log文件的,此时应当结合在调用程序中记录的log信息,下面具体分析一下上述代码:

fopen() 函数使用给定的路径和文件名,新建文件或者打开已有的文件,这取决于最后一个参数, 当使用'a'作为参数时,如果给定的文件不存在,则以此文件名新建文件,并以写'w'方式打开,返回一个文件句柄。

上面代码以天为单位建立日志文件,并且,不同存储过程之间共享log文件,这种方式的优点是可能通过查看log文件追溯出程序的调用顺序和逻辑。实际应用中,应根据不同的需求,具体分析,可以使用更复杂的log文件生成策略。

put_line() 函数用于写入字符到文件,并在字符串的结尾加入换行符,若不想换行,使用put()函数。

new_line() 函数用于生成指定数目的空行,上面对文件的修改写在一个缓冲区内,执行fflush() 将立即将buffer中的内容写入文件,当你希望在文件还未关闭之前就需要读取已经作出的改变时,调用此函数。

is_open() 函数用于判断一个文件句柄的状态,最后用完一定记得把打开的文件关闭,调用fclose() 函数,并且应把这个语句加入exception的处理中,防止过程非正常退出时留下未关闭的文件句柄。

捕获违例

在PLSQL中,你可以通过两个内建的函数sqlcode 和sqlerrm 来找出发生了哪类错误并且获得详细的message信息,在内部违例发生时,sqlcode返回从-1至-20000之间的一个错误号,但有一个例外,仅当内部违例no_data_found 发生时,才会返回一个正数 100。当用户自定义的违例发生时,sqlcode返回+1,除非用户使用 pragma EXCEPTION_INIT 将自定义违例绑定一个自定义的错误号。当没有任何违例抛出时,sqlcode返回0。

下面是一个简单的捕获违例的例子:

declare
i number(3);
begin
select 100/0 into i from dual;
exception
when zero_divide then
...
end;

在上面的exception 中我们使用others 关键字捕获所有未明确指定的违例,并进行记录log处理,同时我们必须在做完这些处理之后,把违例再次抛出给调用程序,调用函数:
raise_application_error (),此函数向调用程序返回一个用户自定义的错误号码和错误信息,第一个参数指定一个错误号码,由用户自行定义,但必须限定在-20000至-20999 之间,避免与Oracle内部定义exception的错误号码冲突,第二个参数需要返回一个字符串,这里我们使用它返回我们上面捕获的错误号码和错误描述。

注意:通过raise_application_error()函数抛出的违例已经不是开始在程序块内部捕获的内部违例,而是由用户自己定义的。

Posted by Alex at 11:34 AM | Comments (0) | Edit | Taged: Oracle (2), 存储过程 (1)

利用XML 与XSL 开发一个易于修改和扩充的用户手册

正文 本文将介绍利用XML与XSL技术内容与表示相分离的特点,创建一个易于扩充,结构灵活的用户手册文档。当然,本文的重点在于阐述一种利用XML的方法,并不仅限于用户手册文档,您可以利用在任何您认为需要的地方。 本文介绍的内容基于XML、XSL与DTD技术规范,您可以使用IE5.0及其以上版本进行测试。 用户的需求 从用户的视角,我们要实现的用户手册包含以下几个部分: 在页面左侧的导航栏,利用树状显示每一个级别的标题,每个标题前按顺序标有标题序号,形如2.1或2.1.8,对于包含子标题的标题要与不包含子标题的标题,即叶子标题区分显示,前面用不同的图片区分。 页面右侧是内容栏,除包含各级标题外,还包含各级标题下的具体内容,内容类型有文字和图片。不同级别的标题要使用不同的现实方式,每个标题前也加有标题序号,与左侧导航栏一致。 对于含有图片的内容部分,图片显示为缩略图,通过点击可以在正常大小与缩略图之间切换。 点击左侧导航栏的标题,可以定位到右侧的相关内容。 对于手册的标题和内容,应该可以方便添加与删除,修改了一个标题的级别,比如从一级标题换到三级标题的位置,其显示风格可以自动更改。理论上,标题的级别可能会有无限多层。 无论是左侧的导航栏,还是右侧的内容区域,用户只对标题的相对位置和级别负责,标题前面的序号要求自动生成。 对应的技术实现 我们打算开发如下几个部分来实现这个用户手册: 一个DTD定义文件,即Document Type Defination(文档类型定义)。定义这个文件需要根据客户对于手册的需求来做,这也是我们需要首先着手去做的部分。...

正文

本文将介绍利用XML与XSL技术内容与表示相分离的特点,创建一个易于扩充,结构灵活的用户手册文档。当然,本文的重点在于阐述一种利用XML的方法,并不仅限于用户手册文档,您可以利用在任何您认为需要的地方。

本文介绍的内容基于XML、XSL与DTD技术规范,您可以使用IE5.0及其以上版本进行测试。

用户的需求

从用户的视角,我们要实现的用户手册包含以下几个部分:

  • 在页面左侧的导航栏,利用树状显示每一个级别的标题,每个标题前按顺序标有标题序号,形如2.1或2.1.8,对于包含子标题的标题要与不包含子标题的标题,即叶子标题区分显示,前面用不同的图片区分。
  • 页面右侧是内容栏,除包含各级标题外,还包含各级标题下的具体内容,内容类型有文字和图片。不同级别的标题要使用不同的现实方式,每个标题前也加有标题序号,与左侧导航栏一致。
  • 对于含有图片的内容部分,图片显示为缩略图,通过点击可以在正常大小与缩略图之间切换。
  • 点击左侧导航栏的标题,可以定位到右侧的相关内容。
  • 对于手册的标题和内容,应该可以方便添加与删除,修改了一个标题的级别,比如从一级标题换到三级标题的位置,其显示风格可以自动更改。理论上,标题的级别可能会有无限多层。
  • 无论是左侧的导航栏,还是右侧的内容区域,用户只对标题的相对位置和级别负责,标题前面的序号要求自动生成。

对应的技术实现

我们打算开发如下几个部分来实现这个用户手册:

  • 一个DTD定义文件,即Document Type Defination(文档类型定义)。定义这个文件需要根据客户对于手册的需求来做,这也是我们需要首先着手去做的部分。
  • 一个XML文档,包含了用户手册的真实的、具体的内容,这个文档当然应该是良构的,需要遵循我们在上面DTD文件中定义的规则。这个文档的内容由手册的编写者来提供,他无需了解其他的技术细节,只需遵照一个简单的规则即可,如果能够使用像XMLSPY这样的工具编辑会更省力。
  • 两个XSL文件,这是我们需要开发的主体部分,也就是决定内容将如何呈现给用户,共有两个XSL文件,分别针对左侧的导航栏和右侧的内容主体。
  • 一个超文本文件,构成用户手册的框架,实现导航栏与内容主体的分割。

定义DTD

通过前面的需求陈述,可以抽象出如下页面元素:

  • 标题,会有不同的级别
  • 内容,包括文本和图片

为此我们定义如下DTD元素:

<!ELEMENT Image (#PCDATA)>
<!ELEMENT Para (#PCDATA)>
<!ELEMENT Name (#PCDATA)>

分别表示图片、段落文本和标题。为了表示标题的嵌套关系,我们又定义一个Item元素,把它作为一个块,不同的Item之间可以是平行关系,也可以是嵌套关系,所有的基本元素(图片、段落文本和标题)必须从属于一个Item。具体的关系描述如下:

  • 一个Item中只能包含、并且必须包含一个标题(Name)
  • 一个Item中可以同时包含多段文字或者多张图片,或者只包含其中一种
  • 一个Item中可以嵌套一个或多个Item,也可以不嵌套任何Item,嵌套的Item具有相同的属性

通过以上描述,我们可以定义元素Item:

<!ELEMENT Item (Name, (Para | Image)*, Item*)>

元素名称后面的'*' 代表此元素可以为0个或者多个。

'(Para | Image)*' 与 'Para*, Image*' 同义。

作为DTD,还必须定义一个根元素,我们起名为'PMS_Help',它将包含Item元素,进而间接的包含了所有的元素。

<!ELEMENT PMS_Help (Item*)>

上面表示为,使用了这个DTD的XML文档中可以定义0个或者多个Item元素。

此外,我们还需要为图片的缩略图定义:

<!ATTLIST Image small ID #IMPLIED>

至此,DTD文件我们就已经定义完了,我们将其保存为PMS_Help.dtd,这里下载完整的DTD文件。

如何准备手册内容

接下来我们要根据上面定义的DTD文件,整理含有实际内容的XML文件,文件内容示例如下:

<PMS_Help>
<Item>
<Name>第一级别标题</Name>
<Item>
<Name>第二级别标题</Name>
<Item>
<Name>第三级别标题</Name>
<Para>段落文本1</Para>
<Image small="image1_small.gif">image1.gif</Image>
<Para>段落文本2</Para>
<Para>段落文本3</Para>
</Item>
<Item>
...
</Item>
</Item>
<Item>
...
</Item>
</Item>       
<Item>
...
</Item>
</PMS_Help>

在第三级标题下面,当然还可以继续嵌套Item元素,有更多级别的标题,只要你在下面的XSL样式文件中对应说明了这个级别标题如何显示即可。

在Para标记之间的文本在显示时会被格式化为一个段落,如果你有多段文本,应该分别用Para进行标记。

Image标记中有一个属性字段small,其值应是一个图片文件名,这个图片用作默认的缩略图显示,当点击这个图片,会显示出Image标记之间的图片文件,当然您也可以对着两个图片使用同一个文件名。图片文件的路径不需要在这里指定,路径是写在下面XSL文件中的。

当我们把包含手册内容的XML文件准备好之后,我们还需要做一个额外的工作,就是将此XML文件复制一份,两个文件分别取名为: PMS_Help.xml, PMS_Help_Lf.xml,两个文件分别绑定不同的XSL样式文件,以取得不同的显示效果,除此以外,两个XML文件是完全相同的。下载两个XML 文件:导航栏XML文件内容栏XML文件

最关键的部分: 开发XSL

1. 首先实现相对简单一点的对应左侧导航栏的XSL文件。

XML文件以一定的结构描述了我们要展现的内容,而在XSL当中,决定了这些内容将以何种方式展现,实际上是XSL是将XML文档格式化为浏览器可以理解的HTML格式文本,在XSL中首先需要定义根级的模板,实际对应了HTML文档的Head以及Body部分,代码如下:

<xsl:template match="PMS_Help">
<html>
<head>
<title>Index</title>
<style media="screen">
<xsl:comment><![CDATA[
body {font-fimaly:'宋体',Arial; font-size:9pt; color:#000000;}
]]></xsl:comment>
</style>
</head>
<body>
<h2>Index</h2>
<p/>
<xsl:for-each select="Item">
<xsl:apply-templates select="."/>
</xsl:for-each>
</body>
</html>
</xsl:template>
 

接下来实现对应处理Item部分的模板:

<xsl:variable name="allParentNode" select="ancestor::Item/Name"/>
<xsl:variable name="allChildNode" select="child::Item/Name"/>
<xsl:variable name="numOfAllParentNode" select="count($allParentNode) + 1"/>
<xsl:variable name="numOfAllChildNode" select="count($allChildNode)"/>
<!-- output the Index of help BEGIN -->
<xsl:element name="div">
<xsl:attribute name="onclick">
window.open('PMS_Help.xml#<xsl:number count="Item" level="multiple" format="01-01-01"/>','main')
<!-- <xsl:value-of select="&quot;alert(this.id)&quot;" /> --></xsl:attribute>
<xsl:attribute name="style"><xsl:value-of select="concat('text-indent:',(($numOfAllParentNode - 1) * 30),';white-space: nowrap')"/></xsl:attribute>
<xsl:attribute name="id"><xsl:number count="Item" level="multiple" format="01-01-01"/></xsl:attribute>
<xsl:attribute name="onmouseover"><xsl:value-of select="&quot; this.style.color='red';this.style.cursor='hand' &quot;"/></xsl:attribute>
<xsl:attribute name="onmouseout"><xsl:value-of select="&quot; this.style.color='black' &quot;"/></xsl:attribute>
<xsl:choose>
<xsl:when test="$numOfAllParentNode = 0">
<img src="images/page/minus.gif" alt="" border="0"/>
</xsl:when>
<xsl:when test="$numOfAllParentNode != 0 and $numOfAllChildNode = 0">
<img src="images/page/passage.gif" alt="" border="0"/>
</xsl:when>
<xsl:otherwise>
<img src="images/page/minus.gif" alt="" border="0"/>
</xsl:otherwise>
</xsl:choose>
<xsl:number count="Item" level="multiple"/>_
<xsl:value-of select="Name"/>
</xsl:element>
<!-- 如果还存在下一级Item,则递归调用此模板 -->
<xsl:if test="count(Item)>0">
<xsl:apply-templates select="Item"/>
</xsl:if>
</xsl:template>

在上面的代码中,我们通过如下的方式定义变量:

<xsl:variable name="allParentNode" select="ancestor::Item/Name"/>

其中,allParentNode是我们为变量取的名字,后面使用select关键字为其赋值,ancestor关键字用于取出后面给定节点的所有父节点,Item/Name是我们在DTD中定义的节点名称。之后我们可以通过 $allParentNode 来引用这个变量。接下来我们使用:

<xsl:variable name="numOfAllParentNode" select="count($allParentNode) + 1"/>

取出当前节点所有父节点的数量。使用以下代码能够在XSL当中实现一个类似case语句的功能,用来判断是否是叶子节点,以显示不同的图片。

<xsl:choose>
<xsl:when test="$numOfAllParentNode = 0">
<img src="images/page/minus.gif" alt="" border="0"/>
</xsl:when>
<xsl:when test="$numOfAllParentNode != 0 and $numOfAllChildNode = 0">
<img src="images/page/passage.gif" alt="" border="0"/>
</xsl:when>
<xsl:otherwise>
<img src="images/page/minus.gif" alt="" border="0"/>
</xsl:otherwise>
</xsl:choose>

由于我们的DTD中不限制Item定义的级别,因此,我们需要递归的处理Item的显示,使用如下语句:

<xsl:if test="count(Item)>0">
<xsl:apply-templates select="Item"/>
</xsl:if> 

我们将此XSL文件保存为PMS_Help_Lf.xsl,下载此文件。

2. 接下来实现对应右侧具体内容展示的XSL。

同样,也需要定义Head和body部分,不同的是,这里定义了更加复杂的样式单(CSS),并增加了一段Javascript脚本:

<xsl:template match="PMS_Help">	
<html>
<head>
<title>Content</title>
<style media="screen, print">
<xsl:comment><![CDATA[
body {font-fimaly:'宋体',Arial; font-size:9pt; color:#000000;}
.Title1 {font-fimaly:'宋体'; font-size:17.2pt; color:#333333; font-weight:bold; margin-left:0pt; background-color:#eeeeee; white-space: nowrap}
.Title2 {font-fimaly:'宋体'; font-size:14pt; color:#000000; font-weight:bold; margin-left:0pt; white-space: nowrap}
.Title3 {font-fimaly:'宋体'; font-size:12pt; color:#333333; font-weight:bold; margin-left:90pt; background-color:#eeeeee; white-space: nowrap}
.Title4 {font-fimaly:'宋体'; font-size:12pt; color:#000000; font-weight:bold; margin-left:90pt; white-space: nowrap}
.Title5 {font-fimaly:'宋体'; font-size:10pt; color:#333333; font-weight:bold; margin-left:160pt; background-color:#eeeeee; white-space: nowrap}
.Title6 {font-fimaly:'宋体'; font-size:10pt; color:#000000; font-weight:bold; margin-left:160pt; white-space: nowrap}
.Para1 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:90pt}
.Para2 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:90pt}
.Para3 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:90pt}
.Para4 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:90pt}
.Para5 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:160pt}
.Para6 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:160pt}
.image {cursor:hand}
]]></xsl:comment>
</style>
<script>
<xsl:comment><![CDATA[
/* 当用户点击图片时,交替显示缩略图和全图 */
function swapImg(which, small, normal){
var s = "/";
//alert(which + '|' + small + '|' + normal);
//alert(which.src.substring(which.src.lastIndexOf(s)+1));
if (which.src.substring(which.src.lastIndexOf(s)+1) == small){
which.src = 'images/' + normal;
which.alt = '===> 缩小 <===';
}
else{
which.src = 'images/' + small;
which.alt = '<=== 放大 ===>';
}
}
]]></xsl:comment>
</script>
</head>
<body>
<a name="top"/>
<h2>Content</h2>
<p align="right">
<a href="mailto:yourname@yourdomain.com">EMail to us</a><xsl:text> </xsl:text><a href="javascript:print()">打印本手册</a>
</p>
<xsl:for-each select="Item">
<xsl:apply-templates select="."/>
</xsl:for-each>
</body>
</html>
</xsl:template>

接下来是处理Item的部分:

<xsl:template match="Item">
<xsl:variable name="allParentNode" select="ancestor::Item/Name"/>
<xsl:variable name="allChildNode" select="child::Item/Name"/>
<xsl:variable name="numOfAllParentNode" select="count($allParentNode) + 1"/>
<xsl:variable name="numOfAllChildNode" select="count($allChildNode)"/>
<!-- output the content of help -->
<!-- 显示各级标题 -->
<xsl:element name="a">
<xsl:attribute name="name"><xsl:number count="Item" level="multiple" format="01-01-01"/></xsl:attribute>
</xsl:element>
<xsl:element name="div">
<xsl:attribute name="class"><xsl:value-of select="concat('Title', $numOfAllParentNode)"/></xsl:attribute>
<xsl:number count="Item" level="multiple"/><xsl:text> </xsl:text>
<xsl:value-of select="Name"/>
<!-- 级别为奇数的标题下加横线 -->
<xsl:if test="$numOfAllParentNode = 1 or ($numOfAllParentNode mod 2) != 0">
::<a href="#top" target="_self">top</a>
<hr size="1" noshade="noshade"/>
</xsl:if>
</xsl:element>
<p/>
<!-- 如果还存在下一级Item,则递归调用此模板 -->
<xsl:if test="count(Item)>0">
<xsl:apply-templates select="Item"/>
</xsl:if>
<!-- 如果不存在下一级标题,显示段落和图片内容 -->
<xsl:if test="count(Item)=0">
<xsl:apply-templates select="Para | Image"/>
</xsl:if>
</xsl:template>
 

为了以不同的样式显示不同级别的标题我们做了如下处理,根据级别的不同,选择不同的样式单(CSS)定义:

<xsl:attribute name="class"><xsl:value-of select="concat('Title', $numOfAllParentNode)"/></xsl:attribute>

额外的,我们在级别为奇数的标题下画上横线:

<!-- 级别为奇数的标题下加横线 -->
<xsl:if test="$numOfAllParentNode = 1 or ($numOfAllParentNode mod 2) != 0">
::<a href="#top" target="_self">top</a>
<hr size="1" noshade="noshade"/>
</xsl:if>

同样的,我们也需要递归调用此模板进行处理:

<!-- 如果还存在下一级Item,则递归调用此模板 -->
<xsl:if test="count(Item)>0">
<xsl:apply-templates select="Item"/>
</xsl:if>

与上一个XSL不同,我们在这里还需要处理Para和Image标记,这些内容在导航栏是不需要显示的。我们定义了模板:

<xsl:template match="Para | Image">
<xsl:variable name="allParentNode" select="ancestor::Item/Name"/>
<xsl:variable name="numOfAllParentNode" select="count($allParentNode) + 1"/>
<!-- 如果节点是Para,显示段落 -->
<xsl:if test="self::Para">
<xsl:element name="div">
<xsl:attribute name="class"><xsl:value-of select="concat('Para', ($numOfAllParentNode) - 1)"/></xsl:attribute>
<p>
<xsl:value-of select="."/>
</p>
</xsl:element>
</xsl:if>

<!-- 如果节点是Image,显示图片 -->
<xsl:if test="self::Image">
<xsl:variable name="small" select="normalize-space(@small)"/>
<xsl:variable name="normal" select="normalize-space(.)"/>
<xsl:element name="div">
<xsl:attribute name="class"><xsl:value-of select="concat('Para', ($numOfAllParentNode) - 1)"/></xsl:attribute>
<xsl:element name="img">
<xsl:choose>
<xsl:when test="boolean($small)">
<xsl:attribute name="src">images/<xsl:value-of select="$small"/></xsl:attribute>
<xsl:attribute name="alt">&lt;=== 放大 ===&gt;</xsl:attribute>
<xsl:attribute name="onclick"><xsl:value-of select="concat('swapImg(this, &quot;',$small,'&quot;,&quot;',$normal,'&quot;)')"/></xsl:attribute>
</xsl:when>
<xsl:when test="not(boolean($small))">
<xsl:attribute name="src">images/<xsl:value-of select="$normal"/></xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:attribute name="class">image</xsl:attribute>
<xsl:attribute name="border">0</xsl:attribute>
</xsl:element>
<p/>
</xsl:element>
</xsl:if>
</xsl:template>

通过onclick属性,我们为图片定义了onclick事件,以处理缩略图与正常大小图片之间的转换:

<xsl:attribute name="onclick"><xsl:value-of select="concat('swapImg(this, &quot;',$small,'&quot;,&quot;',$normal,'&quot;)')"/></xsl:attribute>

这个XSL文件我们保存为PMS_Help.xsl,下载此XSL文件。

将各部分粘合起来

最后写一个HTML文件,定义两个框架,将两个XML文件粘合起来,点此查看此用户手册的效果。

回顾一下,此用户手册包含:

  • 一个DTD文件,用于定义XML文档的结构。此文件在XML文件中被引用,但并不是强制性的,换句话说,你可以根本就不生成这个文件,但实际上你必须遵循一定的规则来编写XML文件以及实现你的XSL样式文件。
  • 两个XML文件,包含了手册的实际内容。这两个文件除了链接的XSL样式文件不同外,其他完全相同。
  • 两个XSL样式文件,在以上的两个XML文件中被引用。一个用于格式化左侧导航栏显示,一个用于格式化右侧具体内容显示。
  • 一个超文本文件,用于粘合两个XML文件。

 

参考资源

  • 下载此用户手册全部代码的打包文件
  • 下载电子教程:XML初学进阶,来自XML中国论坛-http://www.xml.net.cn (域名已失效!)

Posted by Alex at 11:40 AM | Comments (0) | Edit | Taged: xml (1), xsl (1)

Alex's picture

my email address in picture

搜索|Search

订阅更新|Subscribe to Feed

按月归档|By Month

2009
07
2008
11
10
07
05
04
03
02
01
2007
12
10
07
06
05
04
03
02
01
2006
12
11
10
09
08
07
06
05
04
03
02
01
2005
11
10
09
08
07
04
03
2004
12
11
10
09
08
07
06
05
04
03
02
01
2003
12
10
09
08
06
2002
09
08
04
03
02
2001
12
09
07
06
05

我读|My Books

db2

我的链接|My Links

我的朋友|My Friends

Movable Type 4 Logo
Creative Commons License
This blog is licensed under a Creative Commons License.