這個函式庫的來源有兩個, 一是以前用 PHP 寫爬蟲程式時找到的一本好書 :
# 網路機器人, 網路蜘蛛與網路爬蟲-PHP/CURL 程式設計指南 (第二版, 碁峰出版)
Source : 金石堂
其範例程式下載網址如下 :
# http://webbotsspidersscreenscrapers.com/
# http://webbotsspidersscreenscrapers.com/DSP_download.php
我參考了其中的字串剖析函式庫 LIB_parse.php 進行改寫, 裡面的兩個函數 return_between() 與 parse_array() 是此函式庫的主角, 也是進行文本剖析的利器. 另外一個參考來源是網路上的 Python 字串處理函式庫:
# http://docs.python.org/release/2.5.2/lib/string-methods.html
我將這些 PHP 與 Python 字串處理函式改寫為如下之 Javascript 版, 每一個函數都指派為 String 物件的 prototype 屬性以擴充 String 物件的功能, 這樣用法就與 Javascript 字串物件的函數完全一樣了 :
//parse.js
String.prototype.split_string=split_string;
String.prototype.return_between=return_between;
String.prototype.parse_array=parse_array;
String.prototype.remove=remove;
String.prototype.get_attribute=get_attribute;
String.prototype.trim=trim;
String.prototype.ltrim=ltrim;
String.prototype.rtrim=rtrim;
String.prototype.isdigit=isdigit;
String.prototype.startswith=startswith;
String.prototype.endswith=endswith;
String.prototype.title=title;
String.prototype.swapcase=swapcase;
String.prototype.zfill=zfill;
String.prototype.ljust=ljust;
String.prototype.rjust=rjust;
String.prototype.encode=encode;
String.prototype.decode=decode;
/*-----------------------------------------------------------------------------
split_string(delineator, desired, type)
功能 :
此函數會在一個待剖析字串中搜尋指定之界定字串 delineator, 依據所需要的位置
desired, 傳回在該界定字串之前或之後的子字串, 型態 type 用來設定傳回值中是否
要包含界定字串. 此函數所處理之字串與大小寫無關 (包括參數).
參數 :
delineator : 界定字串
desired : "before" : 傳回界定字串前之子字串
"after" : 傳回界定字串後之子字串
type : "incl" : 包含界定字串
"excl" : 不包含界定字串
-----------------------------------------------------------------------------*/
function split_string(delineator, desired, type) {
//處理預設值
var desired = String(desired).toLowerCase() || "before";
var type = String(type).toLowerCase() || "excl";
//為與大小寫無關, 全部轉為小寫處理
var lc_str = String(this).toLowerCase();
var marker = delineator.toLowerCase();
if (lc_str.indexOf(marker, 0) == -1) { //沒找到分割字串:傳回 ""
return "";
}
else { //有找到分割字串
if (desired == "before") { //傳回分割字串前的部分
if (type == "excl") { //不包含分割字串:不必加分割字串本身
var split_here = lc_str.indexOf(marker, 0);
}
else { //包含分割字串:要加計分割字串本身
var split_here = lc_str.indexOf(marker, 0) + marker.length;
}
var parsed_string = this.substring(0, split_here);
}
else { //傳回界定字串後的部分 "after"
if (type == "excl") { //不包含分割字串:要扣除分割字串本身
var split_here = lc_str.indexOf(marker, 0) + marker.length;
}
else { //包含分割字串:要加計分割字串本身
var split_here = lc_str.indexOf(marker, 0);
}
var parsed_string = this.substring(split_here, lc_str.length + 1);
}
}
return parsed_string;
}
/*-----------------------------------------------------------------------------
return_between(start, stop, type)
功能 :
此函數會在待剖析字串字串中搜尋指定之起始字串 start 與結束字串 stop, 傳回在該組
界定字串之間的子字串, 型態 type 用來設定傳回值中是否要包含組界定字串.
此函數所處理之字串與大小寫無關 (包括參數).
參數 :
start : 起始字串
stop : 結束字串
type : "incl" : 包含界定字串
"excl" : 不包含界定字串
-----------------------------------------------------------------------------*/
function return_between(start, stop, type) {
//傳回起始字串後面的部分
var temp = this.split_string(start, "AFTER", type);
//傳回結束字串前面的部分
return temp.split_string(stop, "BEFORE", type);
}
/*-----------------------------------------------------------------------------
parse_array(start, stop)
功能 :
此函數會在待剖析字串字串中重複搜尋指定之起始字串 start 與結束字串 stop 間的
子字串, 並把每次符合的子字串放入陣列中傳回. 傳回值為陣列, 元素中包含界定字串.
此函數所處理之字串與大小寫無關 (包括參數).
參數 :
start : 起始字串
stop : 結束字串
-----------------------------------------------------------------------------*/
function parse_array(start, stop) {
var reg=new RegExp("(" + start + "([\\s\\S]*?)" + stop + ")","ig");
return this.match(reg); //傳回符合之陣列
}
/*-----------------------------------------------------------------------------
remove(start, stop)
功能 :
此函數會在待剖析字串中重複搜尋指定之起始字串 start 與結束字串 stop 間的
子字串, 並把每次符合的子字串從待剖析字串中刪除. 傳回值為刪除後之字串.
注意, 刪除子字串時會連同界定字串一起刪除.
此函數所處理之字串與大小寫無關 (包括參數).
參數 :
start : 起始字串
stop : 結束字串
-----------------------------------------------------------------------------*/
function remove(start, stop) {
var remove_array = this.parse_array(start, stop); //傳回要移除的子字串陣列
var string = this;
if (remove_array != null) { //有找到子字串:執行刪除
for (var i = 0; i < remove_array.length; i++) { //依序移除
var reg = new RegExp(remove_array[i],"ig"); //要刪除之子字串 reg 物件
var string = string.replace(reg, "");
}
return string;
}
else {return this;} //沒有找到子字串:傳回待剖析字串本身
}
/*-----------------------------------------------------------------------------
get_attribute(attribute)
功能 :
此函數會在待剖析 HTML 字串中搜尋指定之屬性值.
參數 :
attribute : HTML 元素之屬性, 例如 style, class 等
-----------------------------------------------------------------------------*/
function get_attribute(attribute) {
var cleaned_html = this.tidy_html(); //整理 tag 元素
cleaned_html = cleaned_html.replace("\r", ""); //去除 CR
cleaned_html = cleaned_html.replace("\n", ""); //去除 LF
var start=attribute.toLowerCase() + "=\""; //屬性開頭 (全小寫)
return cleaned_html.return_between(start, "\"", "EXCL"); //回傳屬性值
}
//=====以下函數來自 VBscript=====//
/*-----------------------------------------------------------------------------
trim()
此函數會刪除字串開頭與結尾處之空白字元後傳回 (與 VBscript 之 Trim 同).
空白字元, 包括空格(space),水平定位(tab),跳頁(form-feed)與換列(linefeed),
相當於 [ \f\n\r\t\v\u00A0\u2028\u2029]
參數 :
無
-----------------------------------------------------------------------------*/
function trim() {
return this.replace(/(^\s*)|(\s*$)/g,"");
}
/*-----------------------------------------------------------------------------
ltrim()
此函數會刪除字串開頭處之空白字元後傳回 (與 VBscript 之 Ltrim 同).
參數 :
無
-----------------------------------------------------------------------------*/
function ltrim() {
return this.replace(/(^\s*)/g, "");
}
/*-----------------------------------------------------------------------------
rtrim()
此函數會刪除字串結尾處之空白字元後傳回 (與 VBscript 之 Rtrim 同).
參數 :
無
-----------------------------------------------------------------------------*/
function rtrim() {
return this.replace(/(\s*$)/g, "");
}
//======以下函數改寫自 Python=====//
//參考 : http://docs.python.org/release/2.5.2/lib/string-methods.html
/*-----------------------------------------------------------------------------
isdigit()
此函數會判斷字串內容是否全為數字 (與 Python 之 isdigit 同).
參數 :
無
-----------------------------------------------------------------------------*/
function isdigit() {
return str.match(/D/)==null;
}
/*-----------------------------------------------------------------------------
startswith(prefix[,start[, end]])
此函數會判斷字串內容是否以 prefix 開頭 (與 Python 之 startwith 同).
備選參數 start 與 end 可指定比對之起訖位置, 預設為從頭 (0) 比到尾 (length-1).
參數 :
無
-----------------------------------------------------------------------------*/
function startswith(prefix, start, end) {
var start=start || 0; //預設值
var end=end || this.length-1; //預設值
var haystack=this.substring(start,end+1);
var needle=new RegExp("^" + prefix,"ig"); //是否以 prefix 結尾 (不分大小寫)
WScript.Echo(haystack);
return needle.test(haystack);
}
/*-----------------------------------------------------------------------------
endswith(prefix[,start[, end]])
此函數會判斷字串內容是否以 prefix 開頭 (與 Python 之 startwith 同).
備選參數 start 與 end 可指定比對之起訖位置, 預設為從頭 (0) 比到尾 (length-1).
參數 :
無
-----------------------------------------------------------------------------*/
function endswith(prefix, start, end) {
var start=start || 0; //預設值
var end=end || this.length-1; //預設值
var haystack=this.substring(start,end+1);
var needle=new RegExp(prefix + "$","ig"); //是否以 prefix 結尾 (不分大小寫)
WScript.Echo(haystack);
return needle.test(haystack);
}
/*-----------------------------------------------------------------------------
title()
此函數會將字串 (英文) 中每一個字的第一個字元改成大寫, 其餘字元改為小寫後傳回
(與 Python 之 title 同).
參數 :
無
-----------------------------------------------------------------------------*/
function title() {
var words=this.replace(/[ ]{2,}/g," ").split(" "); //兩個以上連續空格改為1個
for (var i=0; i<words.length; i++) {
var first=words[i].charAt(0).toUpperCase(); //每字首字元大寫
var others=words[i].substr(1).toLowerCase(); //第2字元至最後小寫
words[i]=first + others; //重組回字
}
return words.join(" "); //重組回字串傳回
};
/*-----------------------------------------------------------------------------
swapcase(prefix[,start[, end]])
此函數會將字串 (英文) 中每一個小寫字元改成大寫, 大寫字元改成小寫後傳回
(與 Python 之 swapcase 同).
參數 :
無
-----------------------------------------------------------------------------*/
function swapcase() {
var arr=[]; //暫存字元用
var lower=/[a-z]/; //小寫字元
var upper=/[A-Z]/; //大寫字元
for (var i=0; i<this.length; i++) { //走訪每一個字元
if (lower.test(this.charAt(i))) { //小寫字母->改成大寫
arr.push(this.charAt(i).toUpperCase());
}
else if (upper.test(this.charAt(i))) { //大寫字母->改成小寫
arr.push(this.charAt(i).toLowerCase());
}
else {arr.push(this.charAt(i));}
}
return arr.join(""); //串接全部字元
};
/*-----------------------------------------------------------------------------
zfill(width)
此函數會將數字字串前面填滿 0 使得總長為 width 字元 (與 Python 之 isalnum 同).
若字串長度小於 width, 則無法填 0 而傳回字串本身.
參數 :
width : 填補之後字串總長度
傳回值 :
字串
-----------------------------------------------------------------------------*/
function zfill(width) {
var zeros=""; //補0字串初始值
if (width < this.length) {return this;} //width小於字串長度:不用補,傳回本身
else { //需要補滿 0
for (var i=0; i<width-this.length; i++) {zeros += "0";} //製作補0字串
return zeros + this; //前面冠上補0字串
}
}
/*-----------------------------------------------------------------------------
ljust(width[, fillchar])
此函數會在字串前面以 fillchar 字元填滿使得總長為 width 字元 (與 Python 之
ljust 同). 若字串長度小於 width, 則無法填滿而傳回字串本身. fillchar 預設為
空格 space=" ".
參數 :
width : 填補之後字串總長度
fillchar : 填補字元
傳回值 :
字串
-----------------------------------------------------------------------------*/
function ljust(width, fillchar) {
var fillchar=fillchar || " "; //填補字元預設值為 " "
var filler=""; //填補字串初始值
if (width < this.length) {return this;} //width小於字串長度:不用補,傳回本身
else { //需要補滿
for (var i=0; i<width-this.length; i++) {filler += fillchar;} //填補字串
return filler + this; //前面冠上填補字串
}
}
/*-----------------------------------------------------------------------------
rjust(width[, fillchar])
此函數會在字串後面以 fillchar 字元填滿使得總長為 width 字元 (與 Python 之
ljust 同). 若字串長度小於 width, 則無法填滿而傳回字串本身. fillchar 預設為
空格 space=" ".
參數 :
width : 填補之後字串總長度
fillchar : 填補字元
傳回值 :
字串
-----------------------------------------------------------------------------*/
function rjust(width, fillchar) {
var fillchar=fillchar || " "; //填補字元預設值為 " "
var filler=""; //填補字串初始值
if (width < this.length) {return this;} //width小於字串長度:不用補,傳回本身
else { //需要補滿
for (var i=0; i<width-this.length; i++) {filler += fillchar;} //填補字串
return this + filler; //後面冠上填補字串
}
}
/*-----------------------------------------------------------------------------
encode(key)
此函數會依據傳入之 key 對字串進行特定編碼. 其相對之解碼函式為 decode(), 必須
傳入相同 key 才會解出原始字串.
參數 :
key : 編碼所用之鍵字串.
傳回值 :
由 key 字串中之字元編碼所組成之加密字串
源碼 : http://www.360doc.com/content/13/0806/13/1073512_305112816.shtml
-----------------------------------------------------------------------------*/
function encode(key) {
if (!key) {
var key="8ABC7DLO5MN6Z9EFGdeJfghijkHIVrstuvwWSTUXYabclmnopqKPQRxyz01234";
}
var nl=this.length;
var t=[];
var a,b,c,x,m=function(y){t[t.length]=key.charAt(y)};
var N=key.length;
var N2=N*N,N5=N*5;
for (x=0;x<nl;x++) {
a=this.charCodeAt(x);
if (a<N5) m(Math.floor(a/N)),m(a%N);
else m(Math.floor(a/N2)+5),m(Math.floor(a/N)%N),m(a%N);
}
var s=t.join("");
return String(s.length).length+String(s.length)+s;
};
/*-----------------------------------------------------------------------------
decode(key)
此函數會依據傳入之 key 對字串進行特定解碼. 其相對之編碼函式為 encode(), 必須
傳入編碼所用之相同 key 才會解出原始字串.
參數 :
key : 解碼所用之鍵字串.
傳回值 :
由 key 字串中之字元解碼所組成之還原字串
源碼 : http://www.360doc.com/content/13/0806/13/1073512_305112816.shtml
-----------------------------------------------------------------------------*/
function decode(key) {
if (!key) {
var key="8ABC7DLO5MN6Z9EFGdeJfghijkHIVrstuvwWSTUXYabclmnopqKPQRxyz01234";
}
var c=this.charAt(0)*1;
if (isNaN(c)) return "";
c=this.substr(1,c)*1;
if (isNaN(c)) return "";
var nl=this.length;
var t=[];
var a,f,b,x=String(c).length+1;
var enc=this;
var m=function(y){return key.indexOf(enc.charAt(y))};
var N=key.length;
if (nl!=x+c) return "";
while (x<nl) {
a=m(x++)
if (a<5)f=a*N+m(x);
else f=(a-5)*N*N+m(x)*N+m(x+=1);
t[t.length]=String.fromCharCode(f);
x++;
}
return t.join("");
};
其中最重要的函數是 return_between(start, end, type), 它使用正規表達式來取得目標字串, 第一個參數 start 是要擷取之資料前面之起始標記; 第二個參數 end 為結束標記, type 用來指定傳回結果是否要包含起始與結束標記, "incl" 為包含, "excl" 為不包含.
將此函式庫放在專案的 /lib 資料夾下 :
下面是這些函數的測試範例 :
範例 10 : 用 return_between() 擷取子字串 [原始碼]
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery UI Single Page Application</title>
<script src="jquery/jquery.js"></script>
<script src="jquery/jquery-ui.js"></script>
<script src="jquery/datatables.js"></script>
<script src="lib/file.js"></script>
<script src="lib/parse.js"></script>
<link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
<link href="jquery/datatables.css" rel="stylesheet">
<style>
body {
font-family: Arial, Helvetica, sans-serif;
font-size:10px;
}
</style>
</head>
<body>
<!-- 頁嵌面板 -->
<div id="tabs">
<ul>
<li><a href="#tabs-1">tab1</a></li>
<li><a href="#tabs-2">tab2</a></li>
<li><a href="#tabs-3">tab3</a></li>
</ul>
<div id="tabs-1">
<div id="data" class="ui-state-highlight ui-corner-all" style="margin-top: 10px; margin-bottom: 10px;padding: 5px;">align="center" height="25"</div>
<button id="get_align" class="ui-button ui-widget ui-corner-all">擷取 align</button>
<button id="get_height" class="ui-button ui-widget ui-corner-all">擷取 height</button>
<script>
$(function(){
//監聽按鈕之 click 事件
$("#get_align").click(function(e) {
e.preventDefault();
var data=$("#data").html();
var str=data.return_between('align="', '"', 'excl');
$("#msgbox").html(str);
$("#msgbox").dialog("open");
});
//監聽按鈕之 click 事件
$("#get_height").click(function(e) {
e.preventDefault();
var data=$("#data").html();
var str=data.return_between('height="', '"', 'excl');
$("#msgbox").html(str);
$("#msgbox").dialog("open");
});
});
</script>
</div>
<div id="tabs-2">
</div>
<div id="tabs-3">
</div>
</div>
<script>
$(function(){
$("#tabs" ).tabs();
});
</script>
<!-- 訊息盒 -->
<div id="msgbox" title="訊息"></div>
<script>
$(function(){
//建立訊息盒
$("#msgbox").dialog({
title: "訊息",
autoOpen: false,
buttons: {
"確定": function() {$(this).dialog("close");}
}
});
});
</script>
</body>
</html>
此網頁在 id=data 的 div 元素中放置字串內容 align="center" height="25", 按下按鈕後會呼叫 return_between() 分別擷取 align 與 height 之屬性值, 結果如下 :
可見 return_between() 可以擷取起始與結束標記之間的子字串.
下面的範例則是測試 parse_array() 函數, 此函數會將字串中多組由起始與結束標記包起來子字串都擷取出來, 放在陣列中傳回 :
範例 11 : 用 parse_array() 擷取子字串陣列
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery UI Single Page Application</title>
<script src="jquery/jquery.js"></script>
<script src="jquery/jquery-ui.js"></script>
<script src="jquery/datatables.js"></script>
<script src="lib/file.js"></script>
<script src="lib/parse.js"></script>
<link id="theme" href="jquery/themes/hot-sneaks/jquery-ui.css" rel="stylesheet">
<link href="jquery/datatables.css" rel="stylesheet">
<style>
body {
font-family: Arial, Helvetica, sans-serif;
font-size:10px;
}
</style>
</head>
<body>
<!-- 頁嵌面板 -->
<div id="tabs">
<ul>
<li><a href="#tabs-1">tab1</a></li>
<li><a href="#tabs-2">tab2</a></li>
<li><a href="#tabs-3">tab3</a></li>
</ul>
<div id="tabs-1">
<input id="data" style="width: 100%; margin-top: 10px; margin-bottom: 10px" value="<td>1</td><td>2</td><td>3</td><td>4</td><td>5</td>"><br>
<input id="out" style="width: 100%; margin-top: 10px; margin-bottom: 10px"<br>
<button id="get_cell_content" class="ui-button ui-widget ui-corner-all">擷取儲存格中的內容</button>
<script>
$(function(){
//監聽按鈕之 click 事件
$("#get_cell_content").click(function(e) {
var data=$("#data").val();
var arr=data.parse_array('<td>', '</td>');
$("#out").val(arr.join());
});
});
</script>
</div>
<div id="tabs-2">
</div>
<div id="tabs-3">
</div>
</div>
<script>
$(function(){
$("#tabs" ).tabs();
});
</script>
</body>
</html>
此例中有兩個 input 文字欄位, id=data 的欄位存放待剖析之字串; 而 id=out 的欄位則用來顯示 parse_array() 傳回來的陣列 (用 join 方法轉成字串), 結果如下 :
可見 parse_array() 在擷取子字串時不會刪除起始與結束標記, 這可以呼叫 replace() 方法處理.
沒有留言 :
張貼留言