2011年9月16日 星期五

phpexcel-寫入---轉錄-ZEAL_BLOG

setOffice2003Compatibility(true);

//*************************************
//设置文档基本属性
$objProps = $objExcel->getProperties();
$objProps->setCreator("Zeal Li");
$objProps->setLastModifiedBy("Zeal Li");
$objProps->setTitle("Office XLS Test Document");
$objProps->setSubject("Office XLS Test Document, Demo");
$objProps->setDescription("Test document, generated by PHPExcel.");
$objProps->setKeywords("office excel PHPExcel");
$objProps->setCategory("Test");

//*************************************
//设置当前的sheet索引,用于后续的内容操作。
//一般只有在使用多个sheet的时候才需要显示调用。
//缺省情况下,PHPExcel会自动创建第一个sheet被设置SheetIndex=0
$objExcel->setActiveSheetIndex(0);


$objActSheet = $objExcel->getActiveSheet();

//设置当前活动sheet的名称
$objActSheet->setTitle('测试Sheet');

//*************************************
//设置单元格内容
//
//由PHPExcel根据传入内容自动判断单元格内容类型
$objActSheet->setCellValue('A1', '字符串内容'); // 字符串内容
$objActSheet->setCellValue('A2', 26); // 数值
$objActSheet->setCellValue('A3', true); // 布尔值
$objActSheet->setCellValue('A4', '=SUM(A2:A2)'); // 公式

//显式指定内容类型
$objActSheet->setCellValueExplicit('A5', '847475847857487584',
PHPExcel_Cell_DataType::TYPE_STRING);

//合并单元格
$objActSheet->mergeCells('B1:C22');

//分离单元格
$objActSheet->unmergeCells('B1:C22');

//*************************************
//设置单元格样式
//

//设置宽度
$objActSheet->getColumnDimension('B')->setAutoSize(true);
$objActSheet->getColumnDimension('A')->setWidth(30);

$objStyleA5 = $objActSheet->getStyle('A5');

//设置单元格内容的数字格式。
//
//如果使用了 PHPExcel_Writer_Excel5 来生成内容的话,
//这里需要注意,在 PHPExcel_Style_NumberFormat 类的 const 变量定义的
//各种自定义格式化方式中,其它类型都可以正常使用,但当setFormatCode
//为 FORMAT_NUMBER 的时候,实际出来的效果被没有把格式设置为"0"。需要
//修改 PHPExcel_Writer_Excel5_Format 类源代码中的 getXf($style) 方法,
//在 if ($this->_BIFF_version == 0x0500) { (第363行附近)前面增加一
//行代码:
//if($ifmt === '0') $ifmt = 1;
//
//设置格式为PHPExcel_Style_NumberFormat::FORMAT_NUMBER,避免某些大数字
//被使用科学记数方式显示,配合下面的 setAutoSize 方法可以让每一行的内容
//都按原始内容全部显示出来。
$objStyleA5
->getNumberFormat()
->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_NUMBER);

//设置字体
$objFontA5 = $objStyleA5->getFont();
$objFontA5->setName('Courier New');
$objFontA5->setSize(10);
$objFontA5->setBold(true);
$objFontA5->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);
$objFontA5->getColor()->setARGB('FF999999');

//设置对齐方式
$objAlignA5 = $objStyleA5->getAlignment();
$objAlignA5->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
$objAlignA5->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);

//设置边框
$objBorderA5 = $objStyleA5->getBorders();
$objBorderA5->getTop()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
$objBorderA5->getTop()->getColor()->setARGB('FFFF0000'); // color
$objBorderA5->getBottom()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
$objBorderA5->getLeft()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);
$objBorderA5->getRight()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);

//设置填充颜色
$objFillA5 = $objStyleA5->getFill();
$objFillA5->setFillType(PHPExcel_Style_Fill::FILL_SOLID);
$objFillA5->getStartColor()->setARGB('FFEEEEEE');

//从指定的单元格复制样式信息.
$objActSheet->duplicateStyle($objStyleA5, 'B1:C22');


//*************************************
//添加图片
$objDrawing = new PHPExcel_Worksheet_Drawing();
$objDrawing->setName('ZealImg');
$objDrawing->setDescription('Image inserted by Zeal');
$objDrawing->setPath('./zeali.net.logo.gif');
$objDrawing->setHeight(36);
$objDrawing->setCoordinates('C23');
$objDrawing->setOffsetX(10);
$objDrawing->setRotation(15);
$objDrawing->getShadow()->setVisible(true);
$objDrawing->getShadow()->setDirection(36);
$objDrawing->setWorksheet($objActSheet);


//添加一个新的worksheet
$objExcel->createSheet();
$objExcel->getSheet(1)->setTitle('测试2');

//保护单元格
$objExcel->getSheet(1)->getProtection()->setSheet(true);
$objExcel->getSheet(1)->protectCells('A1:C22', 'PHPExcel');


//*************************************
//输出内容
//
$outputFileName = "output.xls";
//到文件
////$objWriter->save($outputFileName);
//or
//到浏览器
////header("Content-Type: application/force-download");
////header("Content-Type: application/octet-stream");
////header("Content-Type: application/download");
////header('Content-Disposition:inline;filename="'.$outputFileName.'"');
////header("Content-Transfer-Encoding: binary");
////header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
////header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
////header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
////header("Pragma: no-cache");
////$objWriter->save('php://output');

?>

2011年9月8日 星期四

Mysql日期和時間函數不求人

引述自http://www.webasp.net/article/25/24538.htm
最近為了sql的日期在煩惱,剛好google 到了一篇mysql的日期運用,
po上來給大家參考~

這裡是一個使用日期函數的例子。下面的查詢選擇了所有記錄,其date_col的值是在最後30天以內: 
mysql> SELECT something FROM table WHERE TO_DAYS(NOW()) - TO_DAYS(date_col) <= 30; 

DAYOFWEEK(date) 
返回日期date的星期索引(1=星期天,2=星期一, ……7=星期六)。這些索引值對應於ODBC標準。 
mysql> select DAYOFWEEK('1998-02-03'); 
-> 3 

WEEKDAY(date) 
返回date的星期索引(0=星期一,1=星期二, ……6= 星期天)。 
mysql> select WEEKDAY('1997-10-04 22:23:00'); 
-> 5 
mysql> select WEEKDAY('1997-11-05'); 
-> 2 

DAYOFMONTH(date) 
返回date的月份中日期,在1到31範圍內。 
mysql> select DAYOFMONTH('1998-02-03'); 
-> 3 

DAYOFYEAR(date) 
返回date在一年中的日數, 在1到366範圍內。 
mysql> select DAYOFYEAR('1998-02-03'); 
-> 34 

MONTH(date) 
返回date的月份,範圍1到12。 
mysql> select MONTH('1998-02-03'); 
-> 2 

DAYNAME(date) 
返回date的星期名字。 
mysql> select DAYNAME("1998-02-05"); 
-> 'Thursday' 

MONTHNAME(date) 
返回date的月份名字。 
mysql> select MONTHNAME("1998-02-05"); 
-> 'February' 

QUARTER(date) 
返回date一年中的季度,範圍1到4。 
mysql> select QUARTER('98-04-01'); 
-> 2 

WEEK(date) 
  
WEEK(date,first) 
對於星期天是一週的第一天的地方,有一個單個參數,返回date的週數,範圍在0到52。2個參數形式WEEK()允許
你指定星期是否開始於星期天或星期一。如果第二個參數是0,星期從星期天開始,如果第二個參數是1,
從星期一開始。 
mysql> select WEEK('1998-02-20'); 
-> 7 
mysql> select WEEK('1998-02-20',0); 
-> 7 
mysql> select WEEK('1998-02-20',1); 
-> 8 

YEAR(date) 
返回date的年份,範圍在1000到9999。 
mysql> select YEAR('98-02-03'); 
-> 1998 

HOUR(time) 
返回time的小時,範圍是0到23。 
mysql> select HOUR('10:05:03'); 
-> 10 

MINUTE(time) 
返回time的分鐘,範圍是0到59。 
mysql> select MINUTE('98-02-03 10:05:03'); 
-> 5 

SECOND(time) 
回來time的秒數,範圍是0到59。 
mysql> select SECOND('10:05:03'); 
-> 3 

PERIOD_ADD(P,N) 
增加N個月到階段P(以格式YYMM或YYYYMM)。以格式YYYYMM返回值。注意階段參數P不是日期值。 
mysql> select PERIOD_ADD(9801,2); 
-> 199803 

PERIOD_DIFF(P1,P2) 
返回在時期P1和P2之間月數,P1和P2應該以格式YYMM或YYYYMM。注意,時期參數P1和P2不是日期值。 
mysql> select PERIOD_DIFF(9802,199703); 
-> 11 

DATE_ADD(date,INTERVAL expr type) 
 
DATE_SUB(date,INTERVAL expr type) 
 
ADDDATE(date,INTERVAL expr type) 
 
SUBDATE(date,INTERVAL expr type) 
這些功能執行日期運算。對於MySQL 3.22,他們是新的。ADDDATE()和SUBDATE()是DATE_ADD()和DATE_SUB()的同義詞。
在MySQL 3.23中,你可以使用+和-而不是DATE_ADD()和DATE_SUB()。(見例子)date是一個指定開始日期的
DATETIME或DATE值,expr是指定加到開始日期或從開始日期減去的間隔值一個表達式,expr是一個字符串;它可以以
一個「-」開始表示負間隔。type是一個關鍵詞,指明表達式應該如何被解釋。EXTRACT(type FROM date)函數從日期
中返回「type」間隔。下表顯示了type和expr參數怎樣被關聯: type值 含義 期望的expr格式 
SECOND 秒 SECONDS 
MINUTE 分鐘 MINUTES 
HOUR 時間 HOURS 
DAY 天 DAYS 
MONTH 月 MONTHS 
YEAR 年 YEARS 
MINUTE_SECOND 分鐘和秒 "MINUTES:SECONDS" 
HOUR_MINUTE 小時和分鐘 "HOURS:MINUTES" 
DAY_HOUR 天和小時 "DAYS HOURS" 
YEAR_MONTH 年和月 "YEARS-MONTHS" 
HOUR_SECOND 小時, 分鐘, "HOURS:MINUTES:SECONDS" 
DAY_MINUTE 天, 小時, 分鐘 "DAYS HOURS:MINUTES" 
DAY_SECOND 天, 小時, 分鐘, 秒 "DAYS HOURS:MINUTES:SECONDS" 

MySQL在expr格式中允許任何標點分隔符。表示顯示的是建議的分隔符。如果date參數是一個DATE值並且你的計算僅僅
包含YEAR、MONTH和DAY部分(即,沒有時間部分),結果是一個DATE值。否則結果是一個DATETIME值。 

mysql> SELECT "1997-12-31 23:59:59" + INTERVAL 1 SECOND; 
-> 1998-01-01 00:00:00 
mysql> SELECT INTERVAL 1 DAY + "1997-12-31"; 
-> 1998-01-01 
mysql> SELECT "1998-01-01" - INTERVAL 1 SECOND; 
-> 1997-12-31 23:59:59 
mysql> SELECT DATE_ADD("1997-12-31 23:59:59", 
INTERVAL 1 SECOND); 
-> 1998-01-01 00:00:00 
mysql> SELECT DATE_ADD("1997-12-31 23:59:59", 
INTERVAL 1 DAY); 
-> 1998-01-01 23:59:59 
mysql> SELECT DATE_ADD("1997-12-31 23:59:59", 
INTERVAL "1:1" MINUTE_SECOND); 
-> 1998-01-01 00:01:00 
mysql> SELECT DATE_SUB("1998-01-01 00:00:00", 
INTERVAL "1 1:1:1" DAY_SECOND); 
-> 1997-12-30 22:58:59 
mysql> SELECT DATE_ADD("1998-01-01 00:00:00", 
INTERVAL "-1 10" DAY_HOUR); 
-> 1997-12-30 14:00:00 
mysql> SELECT DATE_SUB("1998-01-02", INTERVAL 31 DAY); 
-> 1997-12-02 
mysql> SELECT EXTRACT(YEAR FROM "1999-07-02"); 
-> 1999 
mysql> SELECT EXTRACT(YEAR_MONTH FROM "1999-07-02 01:02:03"); 
-> 199907 
mysql> SELECT EXTRACT(DAY_MINUTE FROM "1999-07-02 01:02:03"); 
-> 20102 

如果你指定太短的間隔值(不包括type關鍵詞期望的間隔部分),MySQL假設你省掉了間隔值的最左面部分。例如,
如果你指定一個type是DAY_SECOND,值expr被希望有天、小時、分鐘和秒部分。如果你像"1:10"這樣指定值,
MySQL假設日子和小時部分是丟失的並且值代表分鐘和秒。換句話說,"1:10" DAY_SECOND以它等價於"1:10" MINUTE_SECOND
的方式解釋,這對那MySQL解釋TIME值表示經過的時間而非作為一天的時間的方式有二義性。如果你使用確實不正確的日期,
結果是NULL。如果你增加MONTH、YEAR_MONTH或YEAR並且結果日期大於新月份的最大值天數,日子在新月用最大的天調整。 

mysql> select DATE_ADD('1998-01-30', Interval 1 month); 
-> 1998-02-28 
注意,從前面的例子中詞INTERVAL和type關鍵詞不是區分大小寫的。 

TO_DAYS(date) 
給出一個日期date,返回一個天數(從0年的天數)。 
mysql> select TO_DAYS(950501); 
-> 728779 
mysql> select TO_DAYS('1997-10-07'); 
-> 729669 

TO_DAYS()不打算用於使用格列高裡歷(1582)出現前的值。 

FROM_DAYS(N) 
給出一個天數N,返回一個DATE值。 
mysql> select FROM_DAYS(729669); 
-> '1997-10-07' 

TO_DAYS()不打算用於使用格列高裡歷(1582)出現前的值。 

DATE_FORMAT(date,format) 
根據format字符串格式化date值。下列修飾符可以被用在format字符串中: %M 月名字(January……December) 
%W 星期名字(Sunday……Saturday) 
%D 有英語前綴的月份的日期(1st, 2nd, 3rd, 等等。) 
%Y 年, 數字, 4 位 
%y 年, 數字, 2 位 
%a 縮寫的星期名字(Sun……Sat) 
%d 月份中的天數, 數字(00……31) 
%e 月份中的天數, 數字(0……31) 
%m 月, 數字(01……12) 
%c 月, 數字(1……12) 
%b 縮寫的月份名字(Jan……Dec) 
%j 一年中的天數(001……366) 
%H 小時(00……23) 
%k 小時(0……23) 
%h 小時(01……12) 
%I 小時(01……12) 
%l 小時(1……12) 
%i 分鐘, 數字(00……59) 
%r 時間,12 小時(hh:mm:ss [AP]M) 
%T 時間,24 小時(hh:mm:ss) 
%S 秒(00……59) 
%s 秒(00……59) 
%p AM或PM 
%w 一個星期中的天數(0=Sunday ……6=Saturday ) 
%U 星期(0……52), 這裡星期天是星期的第一天 
%u 星期(0……52), 這裡星期一是星期的第一天 
%% 一個文字「%」。 

所有的其他字符不做解釋被覆制到結果中。 

mysql> select DATE_FORMAT('1997-10-04 22:23:00', '%W %M %Y'); 
-> 'Saturday October 1997' 
mysql> select DATE_FORMAT('1997-10-04 22:23:00', '%H:%i:%s'); 
-> '22:23:00' 
mysql> select DATE_FORMAT('1997-10-04 22:23:00', 
'%D %y %a %d %m %b %j'); 
-> '4th 97 Sat 04 10 Oct 277' 
mysql> select DATE_FORMAT('1997-10-04 22:23:00', 
'%H %k %I %r %T %S %w'); 
-> '22 22 10 10:23:00 PM 22:23:00 00 6' 
MySQL3.23中,在格式修飾符字符前需要%。在MySQL更早的版本中,%是可選的。 

TIME_FORMAT(time,format) 
這象上面的DATE_FORMAT()函數一樣使用,但是format字符串只能包含處理小時、分鐘和秒的那些格式修飾符。
其他修飾符產生一個NULL值或0。 
CURDATE() 
 
CURRENT_DATE 
以'YYYY-MM-DD'或YYYYMMDD格式返回今天日期值,取決於函數是在一個字符串還是數字上下文被使用。 
mysql> select CURDATE(); 
-> '1997-12-15' 
mysql> select CURDATE() + 0; 
-> 19971215 

CURTIME() 
 
CURRENT_TIME 
以'HH:MM:SS'或HHMMSS格式返回當前時間值,取決於函數是在一個字符串還是在數字的上下文被使用。 
mysql> select CURTIME(); 
-> '23:50:26' 
mysql> select CURTIME() + 0; 
-> 235026 

NOW() 
 
SYSDATE() 
 
CURRENT_TIMESTAMP 
以'YYYY-MM-DD HH:MM:SS'或YYYYMMDDHHMMSS格式返回當前的日期和時間,取決於函數是在一個字符串還是在數字的
上下文被使用。 
mysql> select NOW(); 
-> '1997-12-15 23:50:26' 
mysql> select NOW() + 0; 
-> 19971215235026 

UNIX_TIMESTAMP() 應用:依據時間區間將資料分類


如果想要查看某個時間以來,每10分鐘、半小時或一小時…視的查詢區間而定,可用以下sql達成
select FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(timeColumn)/ timeDivision)*timeDivision) as timeNearTo,count(*) as 'count'
from data
where timeColumn>beginTime
group by timeNearTo
order by timeNearTo desc;
timeColumn→ 要查詢的時間欄位
timeDivision 是指時間區間的時間,
ex. 要以十分鍾為區間,timeDivision=60(秒)*10
半小時→
timeDivision=60(秒)*30......以此類推

beginTime→ 區間分類的起點時間
ex. '2008-12-28 14:00:00'....



UNIX_TIMESTAMP(date) 
如果沒有參數調用,返回一個Unix時間戳記(從'1970-01-01 00:00:00'GMT開始的秒數)。如果UNIX_TIMESTAMP()用一
個date參數被調用,它返回從'1970-01-01 00:00:00' GMT開始的秒數值。date可以是一個DATE字符串、一個DATETIME
字符串、一個TIMESTAMP或以YYMMDD或YYYYMMDD格式的本地時間的一個數字。 
mysql> select UNIX_TIMESTAMP(); 
-> 882226357 
mysql> select UNIX_TIMESTAMP('1997-10-04 22:23:00'); 
-> 875996580 

當UNIX_TIMESTAMP被用於一個TIMESTAMP列,函數將直接接受值,沒有隱含的「string-to-unix-timestamp」變換。 

FROM_UNIXTIME(unix_timestamp) 
以'YYYY-MM-DD HH:MM:SS'或YYYYMMDDHHMMSS格式返回unix_timestamp參數所表示的值,取決於函數是在一個字符串
還是或數字上下文中被使用。 
mysql> select FROM_UNIXTIME(875996580); 
-> '1997-10-04 22:23:00' 
mysql> select FROM_UNIXTIME(875996580) + 0; 
-> 19971004222300 

FROM_UNIXTIME(unix_timestamp,format) 
返回表示 Unix 時間標記的一個字符串,根據format字符串格式化。format可以包含與DATE_FORMAT()函數列出的條
目同樣的修飾符。 
mysql> select FROM_UNIXTIME(UNIX_TIMESTAMP(), 
'%Y %D %M %h:%i:%s %x'); 
-> '1997 23rd December 03:43:30 x' 

SEC_TO_TIME(seconds) 
返回seconds參數,變換成小時、分鐘和秒,值以'HH:MM:SS'或HHMMSS格式化,取決於函數是在一個字符串還是在數字
上下文中被使用。 
mysql> select SEC_TO_TIME(2378); 
-> '00:39:38' 
mysql> select SEC_TO_TIME(2378) + 0; 
-> 3938 

TIME_TO_SEC(time) 
返回time參數,轉換成秒。 
mysql> select TIME_TO_SEC('22:23:00'); 
-> 80580 
mysql> select TIME_TO_SEC('00:39:38'); 
-> 2378

2011年9月5日 星期一

jQuery的表格插件datatables學習總結----轉錄xuhaisoft的空間

DataTables是一个jQuery的表格插件。这是一个高度灵活的工具,依据的基础逐步增强,这将增加先进的互动控制,支持任何HTML表格。官方网站及其下载:http:/www.datatables.net
下载的1.6版本里面内容很全面。

  • 自动分页处理
  • 即时表格数据过滤
  • 数据排序以及数据类型自动检测
  • 自动处理列宽度
  • 可通过CSS定制样式
  • 支持隐藏列
  • 易用
  • 可扩展性和灵活性
  • 国际化
  • 动态创建表格
  • 免费的
使用:
首先。
<title>DataTables example</title>
<style type="text/css" title="currentStyle">
@import "../../media/css/demo_page.css";
@import "../../media/css/demo_table.css";
@import "../examples_support/themes/smoothness/jquery-ui-1.7.2.custom.css";
</style>
<script type="text/javascript" language="javascript" src="../../media/js/jquery.js"></script>
<script type="text/javascript" language="javascript" src="../../media/js/jquery.dataTables.js"></script>
<script type="text/javascript" charset="utf-8">

引入js和css文件。可以在demo里复制。注意路径地址。


第二步

<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$('#example').dataTable( {
"oLanguage": {
"sUrl": "/SSS/dataTables/de_DE.txt"
},
"bStateSave": true,
//"bJQueryUI": true,   //使用jqueryui 。我用的时候显示的不是很好
"sPaginationType": "full_numbers"//分页
} );
} );
</script>


</head>
<body id="dt_example">//此处为body的id

<div id="container" align="center">//*div 里是 table ,table包括thead等,最好按此格式写*
<h1>物品种类管理</h1>
<div id="demo">
<table cellpadding="5" cellspacing="0" border="1" class="display" id="example" align="center">//id 别忘了
<thead>
<tr>
<th>物品编号</th>
<th>物品名称</th>
<th>物品单位</th>
<th>编辑状态</th>
<th>随便</th>
</tr>
</thead>


<tr class="gradeX">//此处可以是gradeA ,gradeX 等,但是gradeB 隔行换色 效果很好
<td>Trident</td>
<td>Internet
Explorer 4.0</td>
<td>Win 95+</td>
<td class="center">4</td>
<td class="center">X</td>
</tr>
<tr class="gradeC">
<td>Trident</td>
<td>Internet
Explorer 5.0</td>
<td>Win 95+</td>
<td class="center">5</td>
<td class="center">C</td>
</tr>
<tr class="gradeA">
<td>Trident</td>
<td>Internet
Explorer 5.5</td>
<td>Win 95+</td>
<td class="center">5.5</td>
<td class="center">A</td>
</tr>
</tbody>
<tfoot>
</tfoot>   
</table>
</div>       
</div>

上面就能创建出如图的效果, 分页。排序。等等。


最后讲讲 各各属性(添加的位置知道吧 O(∩_∩)O~)
//$(document).ready(function() {
    //$('#example').dataTable( {//加载
        //"bPaginate": true,//分页按钮
        //"bLengthChange": true,//每行显示记录数
        //"bFilter": true,//搜索栏
        //"bSort": true,//排序
        //"bInfo": true,//Showing 1 to 10 of 23 entries  总记录数没也显示多少等信息
        //"bAutoWidth": true } );
//} );


//$(document).ready(function() {
    //$('#example').dataTable( {
        //"aaSorting": [[ 4, "desc" ]]//给列表排序 ,第一个参数表示数组 。4 就是css grade那列。第二个参数为 desc或是asc
    //} );
//} );


//$(document).ready(function() {
    //$('#example').dataTable( {
        //"aoColumns": [
        //    /* Engine */   null,  //默认
        //    /* Browser */  null,
        //    /* Platform */ { "bSearchable": false, //不可参与搜索
        //                     "bVisible":    false },//不可见
        //    /* Version */  { "bVisible":    false },//不可见
        //    /* Grade */    null
        //] } );
//} );



//$(document).ready(function() {
    //$('#example').dataTable({
    //});
//} );



//$(document).ready(function() {
    //$('#example').dataTable( {
        //"sDom": '<"top"i>rt<"bottom"flp<"clear">'//这段是自定义布局没搞明白挺复杂的。    *  l - Length changing * f - Filtering input* t - The table!* i - Information* p - Pagination* r - pRocessing* < and > - div elements* <"class" and > - div with a class    * Examples: <"wrapper"flipt>, <lf<t>ip>

    //} );
//} );



//$(document).ready(function() {
//    $('#example').dataTable( {
    //    "bStateSave": true //保存状态到cookie *************** 很重要 , 当搜索的时候页面一刷新会导致搜索的消失。使用这个属性就可避免了
    //} );
//} );

//$(document).ready(function() {
    //$('#example').dataTable( {
        //"sPaginationType": "full_numbers" //分页,一共两种样式 另一种为two_button  是datatables默认
    //} );
//} );

//$(document).ready(function() {
    //$('#example').dataTable( {  //分页信息 不是很难理解。
        //"oLanguage": {
            //"sLengthMenu": "Display _MENU_ records per page",
            //"sZeroRecords": "Nothing found - sorry",
            //"sInfo": "Showing _START_ to _END_ of _TOTAL_ records",
            //"sInfoEmtpy": "Showing 0 to 0 of 0 records",
            //"sInfoFiltered": "(filtered from _MAX_ total records)"
        //}
    //} );
//} )

$(document).ready(function() {
    oTable = $('#example').dataTable({
        "bJQueryUI": true, //可以添加 jqury的ui theme  需要添加css
        "sPaginationType": "full_numbers"
    });
} );

默认的语言是英文的 当然可以国际化:
"sUrl": "/SSS/dataTables/de_DE.txt" 添加个国际化的文件就可以。 名字随便 路径对了就可以。我写的国际化文件内容如下,可以直接复制到txt中使用.

{
"sProcessing": "Bitte warten...",
"sLengthMenu": "显示_MENU_条 ",
"sZeroRecords": "没有您要搜索的内容",
"sInfo": "从_START_ 到 _END_ 条记录——总记录数为 _TOTAL_ 条",
"sInfoEmpty": "记录数为0",
"sInfoFiltered": "(全部记录数 _MAX_  条)",
"sInfoPostFix": "",
"sSearch": "搜索",
"sUrl": "",
"oPaginate": {
"sFirst":    "第一页",
"sPrevious": " 上一页 ",
"sNext":     " 下一页 ",
"sLast":     " 最后一页 "
}
}

轉錄-動態新增按鈕

上面几种方案可以说即便你没有用到jQuery库,你也能相对比较容易的实现。但这种方案相对依赖jQuery的程度更高。而且必须要求jQuery 1.2版以上。低版本jQuery需要插件。
上 面两个方案都是对删除函数动了很多脑筋,换了多种触发、绑定的方式。这个方案不同,可以与平时纯静态的元素一样在domready的时候绑定。但在我们添 加新行的时候我们改动一下,不再想上面那样拼接字符串来添加新行了。这回我们尝试使用复制DOM元素的方式。并且复制的时候连同绑定的事件一起复制,复制 完之后再用find之类的修改内部的元素。
同时,就像这个例子,如果你会把所有元素都删除光,那template这个模板是必须的,如果不会删光,那就未必需要用template了。为了防止被误删,此处我把template设了隐藏。
我使用了jQuery中特有的clone(true)


  1. .template{display:none;}
  1. <trclass="template">
  2.            <td>这里是模板</td>
  3.            <td><button class="del">删除</button></td>
  4.        </tr>
  5.        <tr>
  6.            <td>这行原来就有</td>
  7.            <td><button class="del">删除</button></td>
  8.        </tr>
  9.        <tr>
  10.            <td>这行原来就有</td>
  11.            <td><button class="del">删除</button></td>
  12.        </tr>
  1. jQuery(function($){
  2.    //第五个表格的删除按钮事件绑定
  3.     $("#table5 .del").click(function() {
  4.         $(this).parents("tr").remove();
  5.    });
  6.    //第五个表格的添加按钮事件绑定
  7.     $("#add5").click(function(){
  8.         $("#table5>tbody>tr:eq(0)")
  9.            //连同事件一起复制
  10.             .clone(true)
  11.            //去除模板标记
  12.             .removeClass("template")
  13.            //修改内部元素
  14.             .find("td:eq(0)")
  15.                 .text("新增行")
  16.                 .end()
  17.            //插入表格
  18.             .appendTo($("#table5>tbody"))
  19.    });
  20. });


事件冒泡法
利用事件冒泡的原理,我们给这个按钮的祖先元素绑定事件处理函数。
然后通过event.target这个对象来判断,这个事件是不是我们要找的对象触发的。
通常可以利用一些DOM属性,比如event.target.className、event.target.tagName等之类的来判断。
  1. <td><buttonclass="del">删除</button></td>
  1. jQuery(function($){
  2.    //第四个表格的删除按钮事件绑定
  3.     $("#table4").click(function(e) {
  4.        if (e.target.className=="del"){
  5.             $(e.target).parents("tr").remove();
  6.        };
  7.    });
  8.    //第四个表格的添加按钮事件绑定
  9.     $("#add4").click(function(){
  10.         $("#table4>tbody").append('<tr><td>新增行</td><td><button class="del">删除</button></td></tr>')
  11.    });
  12. });

2011年9月4日 星期日

html實作備忘技巧-固定td-width

當在table內第一行為colspan又要設定td的width時,常會產生因為text長度不一而無法固定欄寬的問題。

例:    <table width='300px'>
             <tr>
                <td colspan='2'></td>
             </tr>
             <tr>
                <td width='200px'></td>
                <td width='100px'></td>
             </tr>
           
此時 td的 width無效果  需要使用
style="table-layout:fixed"   且在第一行增加一空白td



        <table width='300px' style="table-layout:fixed">
             <tr>
                <td width='200px'></td>
                <td width='100px'></td>
             </tr>
             <tr>
                <td colspan='2'></td>
             </tr>
             <tr>
                <td ></td>
                <td ></td>
             </tr>




用 PHP 分析奇摩股市資訊 --轉錄







Using PHP to parse HTML table




最近因為工作需要,站長又重新複習最不屑的 PHP,所以就找了個題目練練手感,順便也接續之前的 Regular Expression 話題

假設你有一個神奇演算法可以推算股市的走向,但是你需要每隔一段時間就把股價記錄下來好進行統計分析,假設你的資料是從
kimo 來的好了:


很顯然,kimo 提供的網頁適合給人看,給程式分析就顯得太過複雜,再說我們只對下面圈起來的部份有興趣

所以說,理想的情形應該是把這表格內的股價資訊抽取出來,存成簡單的文字檔格式,一般來說存成 csv 是最方便的,因為 Excel
也可以很輕鬆的讀取
ok!讓我們開始吧,就以半導體類的股價來實驗看看,不過您有沒有發現一個奇怪的地方?

URL query string 中的數字很可疑,多 reload 幾次 getstock.php,您會發現每次連到半導體類股價的 URL 都不太一樣?



姑且不論 kimo
這樣作的原因為何,為了保險起見還是應該以 getstock.php 中的為準,所以我們的程式第一步就是要從 getstock.php 中取得正確的 URL,再次按右鍵觀察 html 原始碼:

是我們的老朋友 BIG5,這就容易了
因為不知道把「半導體」三個字塞進 regex 裡面會不會有副作用,所以轉成 \xA5\x62\xBE\xC9\xC5\xE9 比較保險,再把之前 HTML to e-mail 一文中的 <img>...</img> Regex pattern
替換成 <a href="...">...</a> 就好了


因為 PHP 採用的是 PCRE ,所以我們把 Atomic Group 替換成 Possessive Quantifiers,效果一樣,但是 pattern 內可以省掉很多括號(注意,這時候縮短 pattern
才有實質意義)
另外還要注意的是,URL query string 中的空白要替換成 '+',這才符合 RFC3986 的規定,綜合以上討論得出的原始碼就是:

<?php

function get_stock_class_url($stock_class, &$url)

{
  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, "http://tw.stock.yahoo.com/h/getclass.php");

  // 以字串傳回結果
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);

  // 支援header重導向(僅3次)
  curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);

  curl_setopt($curl, CURLOPT_MAXREDIRS, 3);
  // 設定超過10秒就算失敗

  curl_setopt($curl, CURLOPT_TIMEOUT, 10);
  // search URL in <a>XXX</a>

  $result = preg_match('/<a' .
                       '(?:\s++(?!href)[A-Z][-:A-Z0-9]*+(?:\s*+=\s*+(?:"[^"]*+"|\'[^\']*+\'|[-.:\w]++))?+)*+' .

                       '\s++href\s*+=\s*+(?>"([^"]*+)"|\'([^\']*+)\'|([-.:\w]++))' .

                       '[^<>]*+>' .
                       $stock_class .
                       '<\/a>/ix',

                       curl_exec($curl),
                       $matches);
  curl_close($curl);

  if($result <= 0)
    return false;
    
  for($i = 1; $i < count($matches); $i++)

  {
    if(!isset($matches[$i]) || strlen($matches[$i]) <= 0)

      continue;
    
    $pos = strrpos($matches[$i], "?");

    if($pos === FALSE)
      continue;
      
    $url = 'http://tw.stock.yahoo.com' . str_replace(" ", "+", $matches[$i]);

    return true;
  }
  return false;
}

// '\xA5\x62\xBE\xC9\xC5\xE9' = 半導體
if(get_stock_class_url('\xA5\x62\xBE\xC9\xC5\xE9', $url))

{
  echo "success!\n";  
}
else
{

  echo "fail!\n";  
}


?>


有了正確的 URL,現在來嘗試抽取表格資訊吧!但是 table 只有一個嗎?再次偷看 kimo 原始碼:



有一堆 table,其中某些可能只是拿來排版用的(沒想到 kimo 還在用這招XD),我們有興趣的 table 裡面有什麼資訊可以判別呢?首先想到的是 tag id,
很抱歉沒有,這邊找不到什麼優雅的解法,就直接判斷 table 裡面有沒有「股票代號」這四個字吧!

這邊站長偷懶一下,直接把所有的 table 抽取出來(不包含巢狀 table),然後逐個搜尋裡面看看有沒有「股價資訊」這四個字,要把所有的 table
抽取出來,用的 pattern 就是:


這看起來比前一篇文章的 pattern 還要複雜,因為這裡用了 unrolling
的技巧,這招相當於 Regex 如來神掌第九式萬佛朝宗,學會了可說是西出陽關無故人(所以耐心看完本篇的您賺到了)

Regex 大師 Jeffrey E.F. Friedl 認為,當文字具備以下結構時可以用 unrolling 技巧優化:

opening normal* (special normal*)* closing

最簡單的例子算是 domain name:

因為這裡的 special part 只有一個字元「.」,您可能還沒什麼感覺,再看看 double quoted string:


比較有感覺了吧?同樣的 html tag 也具備這種特性,您可以自行推導一下,這樣作最大的優點是:
  • 不須要用到多選分支
  • 減少進出 group
所以比對的次數會減少一大半,再配合 Atomic Group or Possessive Quantifiers,差不多就是把油門踩到底了 (^_^)
找到需要的 table 之後,接下來就是拆解 table,有稍微寫過 html 的人都知道表格的組成是:


所以把拆解 table 就是把上面的 pattern 內的 table 替換成 tr, td,重複作兩次而已!最後得到的 source code 就是:


<?php

function get_stock_class_url($stock_class, &$url)
{

  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, "http://tw.stock.yahoo.com/h/getclass.php");

  // 以字串傳回結果
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);

  // 支援header重導向(僅3次)
  curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);

  curl_setopt($curl, CURLOPT_MAXREDIRS, 3);
  // 設定超過10秒就算失敗

  curl_setopt($curl, CURLOPT_TIMEOUT, 10);
  // search URL in <a>XXX</a>

  $result = preg_match('/<a' .
                       '(?:\s++(?!href)[A-Z][-:A-Z0-9]*+(?:\s*+=\s*+(?:"[^"]*+"|\'[^\']*+\'|[-.:\w]++))?+)*+' .

                       '\s++href\s*+=\s*+(?>"([^"]*+)"|\'([^\']*+)\'|([-.:\w]++))' .

                       '[^<>]*+>' .
                       $stock_class .
                       '<\/a>/ix',

                       curl_exec($curl),
                       $matches);
  curl_close($curl);

  if($result <= 0)
    return false;
    
  for($i = 1; $i < count($matches); $i++)

  {
    if(!isset($matches[$i]) || strlen($matches[$i]) <= 0)

      continue;
    
    $pos = strrpos($matches[$i], "?");

    if($pos === FALSE)
      continue;
      
    $url = 'http://tw.stock.yahoo.com' . str_replace(" ", "+", $matches[$i]);

    return true;
  }
  return false;
}


function get_stock_class_table($url)
{
  $curl = curl_init();

  curl_setopt($curl, CURLOPT_URL, $url);
  // 以字串傳回結果

  curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
  // 支援header重導向(僅3次)

  curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);
  curl_setopt($curl, CURLOPT_MAXREDIRS, 3);

  // 假裝從 http://tw.stock.yahoo.com/h/getclass.php click URL
  curl_setopt($curl, CURLOPT_REFERER, 'http://tw.stock.yahoo.com/h/getclass.php');

  // 設定超過10秒就算失敗
  curl_setopt($curl, CURLOPT_TIMEOUT, 10);

  // 取得 html 順便搜尋 <table>...</table>
  $match_result = preg_match_all('/<table[^>]*+>([^<]*+(?:(?!<\/?+table)<[^<]*+)*+)<\/table>/i', 
                           curl_exec($curl),

                           $table_matches,
                           PREG_SET_ORDER);
  curl_close($curl);
  if($match_result === FALSE || $match_result <= 0)

    return false;
  
  // 找出內含股票代號的 table
  for($i = 0; $i < count($table_matches); $i++)

  {
    if(strpos($table_matches[$i][1], '股票代號') > 0)

      break;
  }
  
  if(count($table_matches) <= $i)

    return false;
  
  // search <tr>...</tr>
  $match_result = preg_match_all('/<tr[^>]*+>([^<]*+(?:(?!<\/?+tr)<[^<]*+)*+)<\/tr>/ix', 
                                 $table_matches[$i][1], 
                                 $tr_matches, 
                                 PREG_SET_ORDER);

  if($match_result === FALSE || $match_result <= 0)

    return false;
    
  // search <td>...</td> and print out
  for($i = 0; $i < count($tr_matches); $i++)

  {
    $match_result = preg_match_all('/<td[^>]*+>([^<]*+(?:(?!<\/?+td)<[^<]*+)*+)<\/td>/ix', 
                                   $tr_matches[$i][1], 
                                   $td_matches, 
                                   PREG_SET_ORDER);

    if($match_result === FALSE || $match_result <= 0)

      continue;

    // 移除 <td>...</td> 內的 HTML tag 與換行符號
    echo trim(str_replace("\n", "", preg_replace('/<[^>]++>/', '', $td_matches[1][1])));

    for($j = 2; $j < count($td_matches)-1; $j++)

    {
        echo ',' . trim(preg_replace("/[\n,]/", '', preg_replace('/<[^>]++>/', '', $td_matches[$j][1])));

    }      
    echo "\n";
  }
  
  return true;

}

// '\xA5\x62\xBE\xC9\xC5\xE9' = 半導體
if(get_stock_class_url('\xA5\x62\xBE\xC9\xC5\xE9', $url))

{
  if(!get_stock_class($url))
  {
    echo "get_stock_class() fail!\n";  
    die();  
  }

}
else
{
  echo "get_stock_class_url() fail!\n";  
}


?>
用 Excel 來欣賞一下成果

心得

本篇對於很多人來說應該是相當實用的,例如您可以寫個 MSN 機器人,發現股票掉到停損點時通知您趕緊賣出;但是請注意,假如您真的 24hours 去 kimo 撈資料,您可能會被當成 hacker 因而吃上官司,小心喔!
另外,用 PHP 寫網路機器人也已經有了專書,根據該作者的建議用 TidyHTML 分析 HTML 是比較好的選擇,您可以參考看看,最後祝您使用愉快,Good Luck!