2013-08-25

[轉載] 字符串匹配的 Boyer-Moore 演算法

轉載自:字符串匹配的Boyer-Moore算法 - 阮一峰的网络日志

上一篇文章,我介绍了KMP算法

但是,它并不是效率最高的算法,实际采用并不多。各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法



Boyer-Moore算法不仅效率高,而且构思巧妙,容易理解。1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了这种算法。

下面,我根据Moore教授自己的例子来解释这种算法。


1.



假定字符串为"HERE IS A SIMPLE EXAMPLE",搜索词为"EXAMPLE"。



2.



首先,"字符串"与"搜索词"头部对齐,从尾部开始比较。

这是一个很聪明的想法,因为如果尾部字符不匹配,那么只要一次比较,就可以知道前7个字符(整体上)肯定不是要找的结果。

我们看到,"S"与"E"不匹配。这时,"S"就被称为"坏字符"(bad character),即不匹配的字符。我们还发现,"S"不包含在搜索词"EXAMPLE"之中,这意味着可以把搜索词直接移到"S"的后一位。



3.



依然从尾部开始比较,发现"P"与"E"不匹配,所以"P"是"坏字符"。但是,"P"包含在搜索词"EXAMPLE"之中。所以,将搜索词后移两位,两个"P"对齐。



4.



我们由此总结出"坏字符规则"

后移位数 = 坏字符的位置 - 搜索词中的上一次出现位置

如果"坏字符"不包含在搜索词之中,则上一次出现位置为 -1。

以"P"为例,它作为"坏字符",出现在搜索词的第6位(从0开始编号),在搜索词中的上一次出现位置为4,所以后移 6 - 4 = 2位。再以前面第二步的"S"为例,它出现在第6位,上一次出现位置是 -1(即未出现),则整个搜索词后移 6 - (-1) = 7位。



5.



依然从尾部开始比较,"E"与"E"匹配。



6.



比较前面一位,"LE"与"LE"匹配。



7.



比较前面一位,"PLE"与"PLE"匹配。



8.



比较前面一位,"MPLE"与"MPLE"匹配。我们把这种情况称为"好后缀"(good suffix),即所有尾部匹配的字符串。注意,"MPLE"、"PLE"、"LE"、"E"都是好后缀。



9.



比较前一位,发现"I"与"A"不匹配。所以,"I"是"坏字符"。



10.



根据"坏字符规则",此时搜索词应该后移 2 - (-1)= 3 位。问题是,此时有没有更好的移法?



11.



我们知道,此时存在"好后缀"。所以,可以采用"好后缀规则"

后移位数 = 好后缀的位置 - 搜索词中的上一次出现位置

举例来说,如果字符串"ABCDAB"的后一个"AB"是"好后缀"。那么它的位置是5(从0开始计算,取最后的"B"的值),在"搜索词中的上一次出现位置"是1(第一个"B"的位置),所以后移 5 - 1 = 4位,前一个"AB"移到后一个"AB"的位置。

再举一个例子,如果字符串"ABCDEF"的"EF"是好后缀,则"EF"的位置是5 ,上一次出现的位置是 -1(即未出现),所以后移 5 - (-1) = 6位,即整个字符串移到"F"的后一位。

这个规则有三个注意点:

  1. "好后缀"的位置以最后一个字符为准。假定"ABCDEF"的"EF"是好后缀,则它的位置以"F"为准,即5(从0开始计算)。
  2. 如果"好后缀"在搜索词中只出现一次,则它的上一次出现位置为 -1。比如,"EF"在"ABCDEF"之中只出现一次,则它的上一次出现位置为-1(即未出现)。
  3. 如果"好后缀"有多个,则除了最长的那个"好后缀",其他"好后缀"的上一次出现位置必须在头部。比如,假定"BABCDAB"的"好后缀"是"DAB"、"AB"、"B",请问这时"好后缀"的上一次出现位置是什么?回答是,此时采用的好后缀是"B",它的上一次出现位置是头部,即第0位。这个规则也可以这样表达:如果最长的那个"好后缀"只出现一次,则可以把搜索词改写成如下形式进行位置计算"(DA)BABCDAB",即虚拟加入最前面的"DA"。

回到上文的这个例子。此时,所有的"好后缀"(MPLE、PLE、LE、E)之中,只有"E"在"EXAMPLE"还出现在头部,所以后移 6 - 0 = 6位。



12.



可以看到,"坏字符规则"只能移3位,"好后缀规则"可以移6位。所以,Boyer-Moore算法的基本思想是,每次后移这两个规则之中的较大值。

更巧妙的是,这两个规则的移动位数,只与搜索词有关,与原字符串无关。因此,可以预先计算生成《坏字符规则表》和《好后缀规则表》。使用时,只要查表比较一下就可以了。



13.



继续从尾部开始比较,"P"与"E"不匹配,因此"P"是"坏字符"。根据"坏字符规则",后移 6 - 4 = 2位。



14.



从尾部开始逐位比较,发现全部匹配,于是搜索结束。如果还要继续查找(即找出全部匹配),则根据"好后缀规则",后移 6 - 0 = 6位,即头部的"E"移到尾部的"E"的位置。

(完)

[轉載] 虚数的意义

轉載自:虚数的意义 - 阮一峰的网络日志

有人在Stack Exchange问了一个问题:

"我一直觉得虚数(imaginary number)很难懂。

中学老师说,虚数就是-1的平方根。



可是,什么数的平方等于-1呢?计算器直接显示出错!

直到今天,我也没有搞懂。谁能解释,虚数到底是什么?

它有什么用?"

帖子的下面,很多人给出了自己的解释,还推荐了一篇非常棒的文章《虚数的图解》。我读后恍然大悟,醍醐灌顶,原来虚数这么简单,一点也不奇怪和难懂!

下面,我就用自己的语言,讲述我所理解的虚数。




一、什么是虚数?


首先,假设有一根数轴,上面有两个反向的点:+1和-1。



这根数轴的正向部分,可以绕原点旋转。显然,逆时针旋转180度,+1就会变成-1。



这相当于两次逆时针旋转90度。



因此,我们可以得到下面的关系式:

(+1) * (逆时针旋转90度) * (逆时针旋转90度) = (-1)

如果把+1消去,这个式子就变为:

(逆时针旋转90度)^2 = (-1)

将"逆时针旋转90度"记为 i :

i^2 = (-1)

这个式子很眼熟,它就是虚数的定义公式。

所以,我们可以知道,虚数 i 就是逆时针旋转90度,i 不是一个数,而是一个旋转量。




二、复数的定义


既然 i 表示旋转量,我们就可以用 i ,表示任何实数的旋转状态。



将实数轴看作横轴,虚数轴看作纵轴,就构成了一个二维平面。旋转到某一个角度的任何正实数,必然唯一对应这个平面中的某个点。

只要确定横坐标和纵坐标,比如( 1 , i ),就可以确定某个实数的旋转量(45度)。

数学家用一种特殊的表示方法,表示这个二维坐标:用 + 号把横坐标和纵坐标连接起来。比如,把 ( 1 , i ) 表示成 1 + i 。这种表示方法就叫做复数(complex number),其中 1 称为实数部,i 称为虚数部。

为什么要把二维坐标表示成这样呢,下一节告诉你原因。




三、虚数的作用:加法


虚数的引入,大大方便了涉及到旋转的计算。



比如,物理学需要计算"力的合成"。假定一个力是 3 + i ,另一个力是 1 + 3i ,请问它们的合成力是多少?



根据"平行四边形法则",你马上得到,合成力就是 ( 3 + i ) + ( 1 + 3i ) = ( 4 + 4i )。

这就是虚数加法的物理意义。




四、虚数的作用:乘法


如果涉及到旋转角度的改变,处理起来更方便。



比如,一条船的航向是 3 + 4i 。

如果该船的航向,逆时针增加45度,请问新航向是多少?



45度的航向就是 1 + i 。计算新航向,只要把这两个航向 3 + 4i 与 1 + i 相乘就可以了(原因在下一节解释):

( 3 + 4i ) * ( 1 + i ) = ( -1 + 7i )

所以,该船的新航向是 -1 + 7i 。

如果航向逆时针增加90度,就更简单了。因为90度的航向就是 i ,所以新航向等于:

( 3 + 4i ) * i = ( -4 + 3i )

这就是虚数乘法的物理意义:改变旋转角度。




五、虚数乘法的数学证明


为什么一个复数改变旋转角度,只要做乘法就可以了?

下面就是它的数学证明,实际上很简单。



任何复数 a + bi,都可以改写成旋转半径 r 与横轴夹角 θ 的形式。

假定现有两个复数 a + bi 和 c + di,可以将它们改写如下:

a + bi = r1 * ( cosα + isinα )

c + di = r2 * ( cosβ + isinβ )


这两个复数相乘,( a + bi )( c + di ) 就相当于

r1 * r2 * ( cosα + isinα ) * ( cosβ + isinβ )


展开后面的乘式,得到

cosα * cosβ - sinα * sinβ + i( cosα * sinβ + sinα * cosβ )


根据三角函数公式,上面的式子就等于

cos(α+β) + isin(α+β)


所以,

( a + bi )( c + di ) = r1 * r2 * ( cos(α+β) + isin(α+β) )


这就证明了,两个复数相乘,就等于旋转半径相乘、旋转角度相加。

(完)

[轉載] 調校 SQL 以徹底改善應用程式效能

轉載自:調校 SQL 以徹底改善應用程式效能

有些程式員在撰寫前端的應用程式時,會透過各種 OOP 語言將存取資料庫的 SQL 陳述式串接起來,卻忽略了 SQL 語法的效能問題。版工曾聽過某半導體大廠的新進程式員,所兜出來的一段 PL/SQL 跑了好幾分鐘還跑不完;想當然爾,即使他前端的 AJAX 用得再漂亮,程式效能頂多也只是差強人意而已。以下是版工整理出的一些簡單心得,讓長年鑽究 ASP.NET / JSP / AJAX 等前端應用程式,卻無暇研究 SQL 語法的程式員,避免踩到一些 SQL 的效能地雷。


1、資料庫設計與規劃
  • Primary Key 欄位的長度儘量小,能用 small integer 就不要用 integer。例如員工資料表,若能用員工編號當主鍵,就不要用身分證字號。
  • 一般欄位亦同。若該資料表要存放的資料不會超過 3 萬筆,用 small integer 即可,不必用 integer。
  • 文字資料欄位若長度固定,如:身分證字號,就不要用 varchar 或 nvarchar,應該用 char 或 nchar。
  • 文字資料欄位若長度不固定,如:地址,則應該用 varchar 或 nvarchar。除了可節省儲存空間外,存取磁碟時也會較有效率。
  • 設計欄位時,若其值可有可無,最好也給一個預設值,並設成「不允許 NULL」(一般欄位預設為「允許 NULL」)。因為 SQL Server 在存放和查詢有 NULL 的資料表時,會花費額外的運算動作 [2]。
  • 若一個資料表的欄位過多,應垂直切割成兩個以上的資料表,並用同名的 Primary Key 一對多連結起來,如:Northwind 的 Orders、Order Details 資料表。以避免在存取資料時,以叢集索引掃描時會載入過多的資料,或修改資料時造成互相鎖定或鎖定過久。



2、適當地建立索引
  • 記得自行幫 Foreign Key 欄位建立索引,即使是很少被 JOIN 的資料表亦然。
  • 替常被查詢或排序的欄位建立索引,如:常被當作 WHERE 子句條件的欄位。
  • 用來建立索引的欄位,長度不宜過長,不要用超過 20 個位元組的欄位,如:地址。
  • 不要替內容重複性高的欄位建立索引,如:性別;反之,若重複性低的欄位則適合建立索引,如:姓名。
  • 不要替使用率低的欄位建立索引。
  • 不宜替過多欄位建立索引,否則反而會影響到新增、修改、刪除的效能,尤其是以線上交易 (OLTP) 為主的網站資料庫。
  • 若資料表存放的資料很少,就不必刻意建立索引。否則可能資料庫沿著索引樹狀結構去搜尋索引中的資料,反而比掃描整個資料表還慢。
  • 若查詢時符合條件的資料很多,則透過「非叢集索引」搜尋的效能,可能反而不如整個資料表逐筆掃描。
  • 建立「叢集索引」的欄位選擇至為重要,會影響到整個索引結構的效能。要用來建立「叢集索引」的欄位,務必選擇「整數」型別 (鍵值會較小)、唯一、不可為 NULL。



3、適當地使用索引
  • 有些書籍會提到,使用 LIKE、% 做模糊查詢時,即使您已替某個欄位建立索引 (如下方例子的 CustomerID),但以常數字元開頭才會使用到索引,若以萬用字元 (%) 開頭則不會使用索引,如下所示:
    USE Northwind;
    GO
    SELECT * FROM Orders WHERE CustomerID LIKE 'D%'; --使用索引
    SELECT * FROM Orders WHERE CustomerID LIKE '%D'; --不使用索引
    

    執行完成後按 Ctrl+L,可檢閱如下圖的「執行計畫」。


    圖 1 可看出「查詢最佳化程式」有使用到索引做搜尋


    圖 2 在此的叢集索引掃描,並未直接使用索引,效能上幾乎只等於掃描整個資料表

    但經版工反覆測試,這種語法是否會使用到索引,抑或會逐筆掃描,並非絕對的。仍要看所下的查詢關鍵字,以及欄位內儲存的資料內容而定。但對於儲存資料筆數龐大的資料表,最好還是少用 LIKE 做模糊查詢。

  • 以下的運算子會造成「負向查詢」,常會讓「查詢最佳化程式」無法有效地使用索引,最好能用其他運算子和語法改寫 (經版工測試,並非有負向運算子,就絕對無法使用索引):
    NOT 、 != 、 <> 、 !> 、 !< 、 NOT EXISTS 、 NOT IN 、 NOT LIKE
  • 避免讓 WHERE 子句中的欄位,去做字串串接或數字運算,否則可能導致「查詢最佳化程式」無法直接使用索引,而改採叢集索引掃描 (經版工測試並非絕對)。
  • 資料表中的資料,會依照「叢集索引」欄位的順序存放,因此當您下 BETWEEN、GROUP BY、ORDER BY 時若有包含「叢集索引」欄位,由於資料已在資料表中排序好,因此可提升查詢速度。
  • 若使用「複合索引」,要注意索引順序上的第一個欄位,才適合當作過濾條件。



4、避免在 WHERE 子句中對欄位使用函數

對欄位使用函數,也等於對欄位做運算或串接的動作,一樣可能會讓「查詢最佳化程式」無法有效地使用索引。但真正對效能影響最重大的,是當您的資料表內若有 10 萬筆資料,則在查詢時就需要呼叫函數 10 萬次,這點才是真正的效能殺手。程式員應注意,在系統開發初期可能感覺不出差異,但當系統上線且資料持續累積後,這些語法細節所造成的效能問題就會逐步浮現。

SELECT * FROM Orders WHERE DATEPART(yyyy, OrderDate) = 1996 AND DATEPART(mm, OrderDate)=7
-- 可改成
SELECT * FROM Orders WHERE OrderDate BETWEEN '19960701' AND '19960731'

SELECT * FROM Orders WHERE SUBSTRING(CustomerID, 1, 1) = 'D'
-- 可改成
SELECT * FROM Orders WHERE CustomerID LIKE 'D%'

注意當您在下 UPDATE、DELETE 陳述式時,若有採用 WHERE 子句,也應符合上述原則。



5、AND 與 OR 的使用

在 AND 運算中,「只要有一個」條件有用到索引 (如下方的 CustomerID),即可大幅提升查詢速度,如下圖 3 所示:

SELECT * FROM Orders WHERE CustomerID='VINET' AND Freight=32.3800 
--使用索引,會出現下圖 3 的畫面

SELECT * FROM Orders WHERE Freight=32.3800 
--不使用索引,會出現上圖 2 的畫面


圖 3

但在 OR 運算中,則要「所有的」條件都有可用的索引,才能使用索引來提升查詢速度。因此 OR 運算子的使用必須特別小心。

若您將上方 AND 的範例,邏輯運算子改成 OR 的話,如下所示:

SELECT * FROM Orders WHERE CustomerID='VINET' OR Freight=32.3800

由於無法有效地使用索引,也會出現圖 2 的畫面。

在使用 OR 運算子時,只要有一個條件 (欄位) 沒有可用的索引,則其他所有的條件 (欄位) 都有索引也沒用,只能如圖 2 般,把整個資料表或整個叢集索引都掃描過,以逐筆比對是否有符合條件的資料。


據網路上文件的說法 [1],上述的 OR 運算陳述式,我們還可用 UNION 聯集適當地改善,如下:

SELECT * FROM Orders WHERE CustomerID='VINET'
UNION
SELECT * FROM Orders WHERE Freight=32.3800

此時您再按 Ctrl+L 檢閱「執行計畫」,會發現上半段的查詢會使用索引,但下半段仍用叢集索引掃描,對效能不無小補。



6、適當地使用子查詢

相較於「子查詢 (Subquery)」,若能用 JOIN 完成的查詢,一般會比較建議使用後者。原因除了 JOIN 的語法較容易理解外,在多數的情況下,JOIN 的效能也會比子查詢較佳;但這並非絕對,也有的情況可能剛好相反。

我們知道子查詢可分為「獨立子查詢」和「關聯子查詢」兩種,前者指子查詢的內容可單獨執行,後者則無法單獨執行,亦即外層查詢的「每一次」查詢動作都需要引用內層查詢的資料,或內層查詢的「每一次」查詢動作都需要參考外層查詢的資料。

以下我們看一個比較極端的例子 [2]。若我們希望所有查詢出來的資料,都能另外給一個自動編號,版工我在之前的文章「用 SQL Server 2005 新增的 ROW_NUMBER 函數撰寫 GridView 分頁」中有介紹過,可用 SQL Server 2005 中新增的 ROW_NUMBER 函數輕易地達成,且 ROW_NUMBER 函數還能再加上「分群 (PARTITION BY)」等功能,而且執行效能極佳。


圖 4 將 Orders 資料表的 830 筆資料都撈出來,並在右側給一組自動編號

現在我們要如上圖 4 般,將 Northwind 中 Orders 資料表的 830 筆資料都撈出來,並自動給一組編號,若用 ROW_NUMBER 函數的寫法如下所示,而且效能極佳,只要 2 ms (毫秒),亦即千分之二秒。

SET STATISTICS TIME ON
SELECT OrderID, ROW_NUMBER() OVER(ORDER BY OrderID) AS 編號
FROM dbo.Orders

但如果是在舊版的 SQL Server 2000 中,我們可能得用以下的「子查詢」寫法:

SET STATISTICS TIME ON
SELECT OrderID,
(SELECT COUNT(*) FROM dbo.Orders AS 內圈
WHERE 內圈.OrderID <= 外圈.OrderID) AS 編號
FROM dbo.Orders AS 外圈
ORDER BY 編號

但這種舊寫法,會像先前所提到的,外層查詢的「每一次」查詢動作都需要引用內層查詢的資料。以上方例子而言,外層查詢的每一筆資料,都要等內層查詢「掃描整個資料表」並作比對和計數,因此 830 筆資料每一筆都要重複掃描整個資料表 830 次,所耗用的時間也因此爆增至 170 ms。

若您用相同的寫法,去查詢 AdventureWorks 資料庫中,有 31,465 筆資料的 Sales.SalesOrderHeader 資料表,用 ROW_NUMBER 函數要 677 ms,還不到 1 秒鐘;但用子查詢的話,居然要高達 225,735 ms,將近快 4 分鐘的時間。

雖然這是較極端的範例,但由此可知子查詢的撰寫,在使用上不可不慎,尤其是「關聯子查詢」。程式員在程式開發初期、資料量還很少時感受不到此種 SQL 語法的重大陷阱;但等到系統上線幾個月或一兩年後,可能就會有反應遲緩的現象。



7、其他查詢技巧
  • DISTINCT、ORDER BY 語法,會讓資料庫做額外的計算。此外聯集的使用,若沒有要剔除重複資料的需求,使用 UNION ALL 會比 UNION 更佳,因為後者會加入類似 DISTINCT 的演算法。
  • 在 SQL Server 2005 版本中,存取資料庫物件時,最好明確指定該物件的「結構描述 (Schema)」,也就是使用兩節式名稱。否則若呼叫者的預設 Schema 不是 dbo,則 SQL Server 在執行時,會先尋找該使用者預設 Schema 所搭配的物件,找不到的話才會轉而使用預設的 dbo,會多耗費尋找的時間。例如若要執行一個叫做 dbo.mySP1 的 Stored Procedure,應使用以下的兩節式名稱:
    EXEC dbo.mySP1
    



8、儘可能用 Stored Procedure 取代前端應用程式直接存取資料表

Stored Procedure 除了經過事先編譯、效能較好以外,亦可節省 SQL 陳述式傳遞的頻寬,也方便商業邏輯的重複使用。再搭配自訂函數和 View 的使用,將來若要修改資料表結構、重新切割或反正規化時亦較方便。



9、儘可能在資料來源層,就先過濾資料

使用 SELECT 語法時,儘量避免傳回所有的資料至前端而不設定 WHERE 等過濾條件。雖然 ASP.NET 中 SqlDataSource、ObjectDataSource 控制項的 FilterExpression 可再做篩選,GridView 控制項的 SortExpression 可再做排序,但會多消耗掉資料庫的系統資源、Web server 的記憶體和網路頻寬。最好還是在資料庫和資料來源層,就先用 SQL 條件式篩選出所要的資料。



結論:

本文的觀念,不管是寫 SQL statement、Stored Procedure、自訂函數或 View 皆然。本文只是挑出程式員較容易犯的 SQL 語法效能問題,以期能在短時間瀏覽過本文後,在寫 ADO.NET 程式時能修正以往隨興的 SQL 撰寫習慣。文中提到的幾點,只不過是 SQL 語法效能議題的入門篇。後續有時間的話,版工會再補充在本帖的回應留言,或另開新主題。



參考文件:

[1] SQL查詢最佳化 (網際烏托邦):
http://www.ithome.com.tw/plog/index.php?op=ViewArticle&articleId=5421&blogId=620

參考書籍:

[2] SQL Server 2005 Performance Tuning 效能調校:
作者:胡百敬、姚巧枚、劉承修
出版社:悅知出版社
http://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=9789866761225&sid=41966

[3] SQL Server 2005 完全實戰:
作者:章立民
出版社:碁峰出版社

相關文件:

[4] 臺大醫院資料庫分割疏失,系統幾近停擺 (ITHome):
http://www.ithome.com.tw/itadm/article.php?c=43597

[5] 當DataGrid遇見100萬筆資料:
http://blog.sina.com.tw/4907/article.php?pbgid=4907&entryid=3921

[6] ASP.NET 2.0 GridView 範例集 - 「4-8-4、GridView的效能」:
http://blog.csdn.net/Code6421/archive/2007/12/22/1958167.aspx

[7] 有關開啟頁面時,一次載入數千筆資料的效能問題:
http://www.blueshop.com.tw:80/board/show.asp?subcde=BRD200709141021458MV

2013-08-17

[PHP] url, base64, sprite 三種格式的 icons.css 產生器

先做一個假設,如果 icon 的檔名就是 css 的 class 樣式名稱,那麼我們只要掃瞄資料夾的 Icon 圖檔,然後產生對應的 CSS 檔案,這樣就可以省去製作 Sprite 圖檔跟維護 CSS 對應的問題。

第一種 url 格式只是取得路徑的問題。

第二種 base64 格式可以透過 base64_encode(file_get_contents($path)); 就簡單的達成。

第三種 sprite 格式則使用 Imagick 去處理,會比較快樂。


接著以下就是如何達成的程式片段:

<?php

/*把目錄改變到當前文件下*/
chdir(dirname(__FILE__));

/*Sprite 圖與圖的間距*/
$spriteGap = 30;


/*=[ 取得圖檔資訊 ]=*/
$maxWidth = 0;
$maxHeight = 0;
$nextTop = 0;
$imageList = array();

foreach ( glob('icons/*.{png,jpg,gif}',GLOB_BRACE) as $path )
{
    $image = new Imagick($path);
    $name = pathinfo($path,PATHINFO_FILENAME);

    if(isset($imageList[$name])){ 
        throw new Exception("圖片名稱重複 [ $name ]"); 
    }

    $info = array(
        '{top}' => $nextTop,
        '{image}' => $image,
        '{width}' => $image->getImageWidth(),
        '{height}' => $image->getImageHeight(),
        '{name}' => $name,
        '{path}' => $path,
        '{isAnimate}' => false
    );

    $header = '';
    switch($image->getImageFormat()){
        case "PNG":
            $header = 'data:image/png;base64,'; break;
        case "JPEG":
            $header = 'data:image/jpeg;base64,'; break;
        case "GIF":
            $header = 'data:image/gif;base64,'; break;
        default: break;
    }

    $info['{uri}'] = $header.base64_encode(file_get_contents($path));

    $maxWidth = max($maxWidth, $info['{width}']);
    $maxHeight = max($maxHeight, $info['{height}']);

    
    /*檢查圖片是否為動畫*/
    $frameNum = 0;
    foreach($image->deconstructImages() as $i) {
        $frameNum++;
        if ($frameNum > 1) {
            $info['{isAnimate}'] = true;
            break;
        }
    }
    
    if(!$info['{isAnimate}']){
        $nextTop += $info['{height}'] + $spriteGap;
    }


    $imageList[$name] = $info;
}


/*=[ 製作 CSS Sprite 圖檔 ]=*/
$spriteImage = new Imagick();
$spriteImage->newImage($maxWidth, $nextTop, new ImagickPixel());
$spriteImage->setImageFormat('png');
$spriteImage->paintTransparentImage(new ImagickPixel(), 0.0, 0);

foreach ($imageList as $name => $info)
{
    if($info['{isAnimate}']){ continue; } /* 忽略 GIF 動畫 */

    /* 複製 Icon 圖檔到 Sprite */
    $spriteImage->compositeImage(
        $info['{image}'],
        $info['{image}']->getImageCompose(),
        0,
        $info['{top}']
    );

    $info['{image}']->destroy();
    unset($imageList[$name]['{image}']);
}

$spriteImage->writeImage('icons.sprite.png');
$spriteImage->destroy();
$spriteImage = null;


下載完整程式: php_make_icons_css.zip

[轉載] 公司職稱中英文對照表

轉載自:公司職稱中英文對照表

中文職稱英文職稱英文縮寫
董事長 / 會長Chairman 
副董事長 / 副會長Vice Chairman 
監事Supervisor 
常務監事Managing Supervisor 
董事 / 理事 / 總監Director 
董事總經理Managing DirectorMD
執行董事 / 常務董事Executive DirectorED
副董事 / 助理董事Associate DirectorAD
執行長Chief Executive 
執行長Chief Executive OfficerCEO
財務長Chief Financial OfficerCFO
資訊長Chief Information OfficerCIO
知識長Chief Knowledge OfficerCKO
營運長Chief Operating OfficerCOO
技術長Chief Technology OfficerCTO
管理長Chief Administrative OfficerCAO
行銷長Chief Marketing OfficerCMO
投資長Chief Investment OfficerCIO
總裁 / 社長President 
執行副總裁Executive Vice PresidentEVP
資深副總裁Senior Vice PresidentSVP
副總裁 / 副社長Vice PresidentVP
協理副總裁Associate Vice PresidentAVP
助理副總裁 / 協理Assistant Vice PresidentAVP
總經理General ManagerGM
副總經理Vice General Manager / Deputy General ManageVGM / DGM
副總經理 / 協理Associate General Manager / Assistant General ManagerAGM
執行助理Executive Assistant 
特別助理Special Assistant 
顧問Adviser / Consultant 
處長 / 協理Director 
副處長Vice Director / Deputy Director 
資深經理Senior Manager 
經理Manager 
副理 / 襄理Deputy Manager / Assistant Manager 
襄理Junior Manager / Senior Officer 
部門經理 / 課長Section Manager 
部門副理 / 副課長Deputy Section Manager 
廠長Factory Manager / Plant Manager / Factory Director 
副廠長Deputy Factory Manager 
專案經理Project ManagerPM
主任 / 組長Supervisor / Director / Chief 
業務主任Sales Executive 
專案專員 / 業務專員Account OfficerAO
專案執行Account ExecutiveAE
領班 / 組長Team Leader 
專員Specialist (Officer) 
儲備幹部Management AssociateMA
儲備幹部Management TraineeMT
代表Representative 
管理師Administrator 
會計Accountant 
稽核Auditor 
工程師Engineer 
研究員 / 分析師Analyst 
秘書Secretary 
辦事員 / 事務員Clerk 
職員Staff 
作業員Operator 
技術員Technician 
助理Assistant 

2013-08-07

[MySQL] 計算漢明距離

之前寫了用 PHP 計算圖片的 hash code,接著要怎麼去搜尋相似的圖片呢?

資料庫是一個很好的選擇,首先建立一個簡單的 Table:
CREATE TABLE image_hash (
    id         INT(11) NOT NULL AUTO_INCREMENT,
    filepath   VARCHAR(256) NOT NULL,
    hash       BIGINT(20) UNSIGNED ZEROFILL NOT NULL,

    PRIMARY KEY  (id)
) ENGINE=MYISAM;


再來要怎麼將 hash code 寫進資料表呢?我們可以利用 b'010101' 的 bit 表示語法將 hash code 64位元字串存進 BIGINT 裡面。
INSERT INTO image_hash_info (filepath, hash)
    VALUES('/home/jax/12.jpg', b'10100111...');


資料已經備齊了,最重要的就是怎麼計算漢明距離了,MySQL 提供了 BIT_COUNT() 的函數,可以計算數字中位元 1 的個數,所以剩下的就是 XOR 的運算了。
SELECT BIT_COUNT(29), BIT_COUNT(b'101010');
       -- -> 4, 3


找尋與某一個 hash code 相似的圖片
SELECT * FROM image_hash
    WHERE BIT_COUNT(hash ^ b'1010011110...') <= 10


參考來源:
MySQL 5.0 Bit Functions

2013-08-06

符號英文名稱

_underline底線
,comma逗號
;semicolon分號
:colon冒號
!exclamation mark驚嘆號
?question mark問號
//slash-slash, comment雙斜線, 註釋符
/slash斜線
...ellipsis省略號
.dot句號, 點
`grave accent重音號
^caret插入號
¨tandem colon單點號
~tilde波浪符
""double quotation marks雙引號
''single quotation marks單引號
()brackets, parentheses括號
(left parenthesis, open parenthesis, open paren左圓括號
)right parenthesis, close parenthesis, close paren右圓括號
[]square brackets方括號
[left square bracket, open bracket左方括號
]right square bracket, close bracket右方括號
{}curly brackets or braces大括號
{left curly brace, open brace, open curly左花括號
}right curly brace, close brace, close curly右花括號
@at小老鼠
$dollar sign金錢符號
*star星號
\backslash反斜线
&and
#pound井號
%percent百分號
+plus加號;正號
-minus減號;負號
dash破折號
-hyphen連字號
×is multiplied by乘號
÷is divided by除號
=equal等於
<>angle brackets尖括號
|pipe, vertical bar管線, 豎線
triangle三角形
square root平方根
less than sign小於號
greater than sign大於號
infinity無限號
is approximately equal to約等於號
is equivalent to全等於號
intersection of交集
union of連集
perpendicular to垂直於
angle
the integral of積分
since; because因為
hence所以
arrow箭號;參見號
parallel雙線號
celsius system攝氏度
°degree
σsigma, summation of總和
πpi圓周率
varies as與…成比例
circle

2013-07-07

[轉載][CSS] drop-shadows without images

轉載自:CSS drop-shadows without images

Lifted corners

Curled corners

Perspective

Raised box

Single vertical curve

Vertical curves

Single horizontal curve

Horizontal curves

Rotated box

2013-07-05

[轉載][MySQL] Storage Engines

轉載自:Chapter 14. Pluggable Storage Engine Architecture

第14章:插件式儲存引擎體系結構


目錄
14.1. 前言
14.2. 概述
14.3. 公共MySQL資料庫伺服器層
14.4. 選擇儲存引擎
14.5. 將儲存引擎指定給資料表
14.6. 儲存引擎和事務
14.7. 插入儲存引擎
14.8. 拔出儲存引擎
14.9. 插件式儲存器的安全含義


14.1. 前言


在MySQL 5.1中,MySQL AB引入了新的插件式儲存引擎體系結構,允許將儲存引擎加載到正在運新的MySQL伺服器中。

本章介紹了插件式儲存引擎體系結構,概要介紹了與MySQL一起提供的各種儲存引擎,介紹了將儲存引擎賦給資料表的方法,以及插入和拔出儲存引擎的方法。


14.2. 概述


使用MySQL插件式儲存引擎體系結構,允許資料庫專業人員為特定的應用需求選擇專門的儲存引擎,完全不需要管理任何特殊的應用編碼要求。採用MySQL伺服器體系結構,由於在儲存級別上提供了一致和簡單的應用模型和API,應用程式編程人員和DBA可不再考慮所有的底層實施細節。因此,儘管不同的儲存引擎具有不同的能力,應用程式是與之分離的。

在下圖中,以圖形方式介紹了MySQL插件式儲存引擎體系結構:

圖14.1:MySQL插件式儲存引擎的體系結構
The MySQL pluggable storage engine architecture

插件式儲存引擎體系結構提供了標準的管理和支援服務集合,它們對所有的基本儲存引擎來說是共同的。儲存引擎本身是資料庫伺服器的組件,負責對在物理伺服器層面上維護的基本數據進行實際操作。

這是一種高效的模塊化體系結構,它為那些希望專注於特定應用需求的人員提供了巨大的便利和益處,這類特殊應用需求包括數據倉儲、事務處理、高可用性情形等,同時還能利用獨立於任何儲存引擎的一組接口和服務。

應用程式編程人員和DBA通過位於儲存引擎之上的連接器API和服務層來處理MySQL資料庫。如果應用程式的變化需要改變底層儲存引擎,或需要增加1個或多個額外的儲存引擎以支援新的需求,不需要進行大的編碼或程序更改就能實現這類要求。MySQL伺服器體系結構提供了一致和易於使用的API,這類API適用於多種儲存引擎,通過該方式,該結構將應用程式與儲存引擎的底層複雜性隔離開來。


14.3. 公共MySQL資料庫伺服器層


MySQL插件式儲存引擎是MySQL資料庫伺服器中的組件,負責為資料庫執行實際的數據I/O操作,並能允許和強制執行面向特殊應用需求的特定特性集合。使用特殊儲存引擎的主要優點之一在於,僅需提供特殊應用所需的特性,因此,資料庫中的系統開銷較小,最終結果具有更有效和更高的資料庫性能。這也是MySQL被始終視為具有高性能的原因之一,在行業標準基準方面,它能匹敵或擊敗專有的整體式資料庫。

從技術角度上看,在儲存引擎中,一些獨特的支援底層結構的組件是什麼呢?一些關鍵差別包括:
  • 並發性:某些應用程式比其他應用程式具有很多的顆粒級鎖定要求(如行級鎖定)。選擇正確的鎖定策略能夠減少開銷,並有助於整體性能的提升。它還包括對多種能力的支援,如多版本並發性控制或「快照」讀取等。
  • 事務支援:並非所有的應用程式都需要事務,但對的確需要事務的應用程式來說,有著定義良好的需求,如ACID兼容等。
  • 引用完整性:通過DDL定義的外部鍵,伺服器需要強制保持關聯資料庫的引用完整性。
  • 物理儲存:它包括各種各樣的事項,從資料表和索引的總的頁大小,到儲存數據所需的格式,到物理磁盤。
  • 索引支援:不同的應用程式傾向於採用不同的索引策略,每種儲存引擎通常有自己的編制索引方法,但某些索引方法(如B-tree索引)對幾乎所有的儲存引擎來說是共同的。
  • 內存高速緩衝:與其他應用程式相比,不同的應用程式對某些內存高速緩衝策略的響應更好,因此,儘管某些內存高速緩衝對所有儲存引擎來說是共同的(如用於用戶連接的高速緩衝,MySQL的高速查詢高速緩衝等),其他高速緩衝策略僅當使用特殊的儲存引擎時才唯一定義。
  • 性能幫助:包括針對並行操作的多I/O線程,線程並發性,資料庫檢查點,成批插入處理等。
  • 其他目標特性:可能包括對地理空間操作的支援,對特定數據處理操作的安全限制等。
每組插件式儲存引擎基本組件均採用了相應的設計,能夠為特定應用提供可選擇的特性集合。從反面角度看,避免使用組件特性集合有助於避免不必要的開銷。因此,顯而易見,應理解特定應用程式的需求集合,並選擇恰當的能大幅度改善系統整體效率和性能的MySQL儲存引擎。


14.4. 選擇儲存引擎


與MySQL一起提供的各種儲存引擎在設計時考慮了不同的使用情況。為了更有效地使用插件式儲存體系結構,最好瞭解各種儲存引擎的優點和缺點。

在下面的資料表格中,概要介紹了與MySQL一起提供的儲存引擎:

圖14.2:儲存引擎比較
Storage engine comparison

下述儲存引擎是最常用的:
  • MyISAM:預設的MySQL插件式儲存引擎,它是在Web、數據倉儲和其他應用環境下最常使用的儲存引擎之一。注意,通過更改STORAGE_ENGINE配置變數,能夠方便地更改MySQL伺服器的預設儲存引擎。
  • InnoDB:用於事務處理應用程式,具有眾多特性,包括ACID事務支援。
  • BDB:可替代InnoDB的事務引擎,支援COMMIT、ROLLBACK和其他事務特性。
  • Memory:將所有數據保存在RAM中,在需要快速搜尋引用和其他類似數據的環境下,可提供極快的訪問。
  • Merge:允許MySQL DBA或開發人員將一系列等同的MyISAM資料表以邏輯方式組合在一起,並作為1個對象引用它們。對於諸如數據倉儲等VLDB環境十分適合。
  • Archive:為大量很少引用的歷史、歸檔、或安全審計訊息的儲存和檢索提供了完美的解決方案。
  • Federated:能夠將多個分離的MySQL伺服器連結起來,從多個物理伺服器建立一個邏輯資料庫。十分適合於分佈式環境或數據集市環境。
  • Cluster/NDB:MySQL的叢集式資料庫引擎,尤其適合於具有高性能搜尋要求的應用程式,這類搜尋需求還要求具有最高的正常工作時間和可用性。
  • Other:其他儲存引擎包括CSV(引用由逗號隔開的用作資料庫資料表的檔案),Blackhole(用於臨時禁止對資料庫的應用程式輸入),以及Example引擎(可為快速建立定制的插件式儲存引擎提供幫助)。
請記住,對於整個伺服器或方案,您並不一定要使用相同的儲存引擎,您可以為方案中的每個資料表使用不同的儲存引擎,這點很重要。

關於MySQL中所包含儲存引擎的詳細訊息,請參見14.4節,「選擇儲存引擎」




14.5. 將儲存引擎指定給資料表


可以在建立新資料表時指定儲存引擎,或通過使用ALTER TABLE語句指定儲存引擎。

要想在建立資料表時指定儲存引擎,可使用ENGINE參數:
CREATE TABLE engineTest(
    id INT
) ENGINE = MyISAM;


要想更改已有資料表的儲存引擎,可使用ALTER TABLE語句:
ALTER TABLE engineTest ENGINE = ARCHIVE;


14.6. 儲存引擎和事務


下述儲存引擎支援事務:
  • InnoDB:通過MVCC支援事務,允許COMMIT、ROLLBACK和保存點。
  • NDB:通過MVCC支援事務,允許COMMIT和ROLLBACK。
  • BDB:支援事務,允許COMMIT和ROLLBACK。


14.7. 插入儲存引擎


能夠使用儲存引擎之前,必須使用INSTALL PLUGIN語句將儲存引擎plugin(插件)裝載到mysql。例如,要想加載example引擎,首先應加載ha_example.so模塊:
INSTALL PLUGIN ha_example SONAME 'ha_example.so';

檔案.so必須位於MySQL伺服器庫目錄下(典型情況下是installdir/lib)。


14.8. 拔出儲存引擎


要想拔出儲存引擎,可使用UNINSTALL PLUGIN語句:
UNINSTALL PLUGIN ha_example;

如果拔出了正被已有資料表使用的儲存引擎,這些資料表將成為不可訪問的。拔出儲存引擎之前,請確保沒有任何資料表使用該儲存引擎。


14.9. 插件式儲存器的安全含義


為了安裝插件式儲存引擎,plugin檔案必須位於恰當的MySQL庫目錄下,而且發出INSTALL PLUGIN語句的用戶必須具有SUPER權限。


這是MySQL參考手冊的翻譯版本,關於MySQL參考手冊,請訪問dev.mysql.com。原始參考手冊為英文版,與英文版參考手冊相比,本翻譯版可能不是最新的。

2013-06-10

[轉載] Google Lerry Page 訂立的開會原則 - 2012

轉載自:Google Lerry Page 訂立的開會原則 - 2012

Google 換 Larry Page 當新的 CEO 後, Larry Page 改變了 Google 的開會流程, 定義了開會所需要注意的幾點事項.

Google 訂立開會原則 - Larry Page


內容參考整理自下述兩篇:

Google 訂立的開會原則

  1. 每次開會都要有一個明確的決策者,如果沒有決策者,或沒法作出決定,就不應該開會議。
    (Every meeting must have one clear decision maker. If there's no decision maker -- or no decision to be made -- the meeting shouldn't happen.)
  2. 開會不能超過10個人。
    (No more than 10 people should attend.)
  3. 每個參與會議者都必須要給出自己的想法、意見,否則不應該參加此次的會議。
    (Every person should give input, otherwise they shouldn't be there.)
  4. 任何要訂立的決策都不應該因為會議未招開而延後。如果需要開會才能做出某個決定,那麼該會議應該立刻招開。
    (No decision should ever wait for a meeting. If a meeting absolutely has to happen before a decision should be made, then the meeting should be scheduled immediately.)

2013-06-05

[轉載][MySQL] 修復工具 mysqlcheck

轉載自:mysql修复工具mysqlcheck

mysqlcheck -a -c -o -r -m --all-databases -uroot -p

即可最佳化所有db
參數含意:
  • -a = Analyse given tables.
  • -c = Check table for errors
  • -o = Optimise table
  • -r = Can fix almost anything except unique keys that aren't unique
  • -m = --medium-check


mysqlcheck 客戶端可以檢查和修復 MyISAM 資料表。它還可以優化和分析資料表。

mysqlcheck 的功能類似 myisamchk,但其工作不同。主要差別是當 mysqld 服務器在運行時必須使用mysqlcheck,而 myisamchk 應用於服務器沒有運行時。使用 mysqlcheck 的好處是不需要停止服務器來檢查或修復資料表。使用 myisamchk 修復失敗是不可逆的

Mysqlcheck 為用戶提供了一種方便的使用 SQL 語句 CHECK TABLE、REPAIR TABLE、ANALYZE TABLE和OPTIMIZE TABLE 的方式。它確定在要執行的操作中使用使用哪個語句,然後將語句發送到要執行的服務器上。


有3種方式來調用mysqlcheck:
shell> mysqlcheck [options] db_name [tbl_name ...]
shell> mysqlcheck [options] --databases db_name ...
shell> mysqlcheck [options] --all-databases

如果沒有指定任何資料表或使用 --databases 或 --all-databases 選項,則檢查整個數據庫。

同其它客戶端比較,mysqlcheck 有一個特殊特性。重新命名二進制可以更改檢查資料表的默認行為(--check)。如果你想要一個工具默認可以修復資料表的工具,只需要將 mysqlcheck 重新復制為 mysqlrepair,或者使用一個符號鏈接 mysqlrepair 鏈接 mysqlcheck。如果調用 mysqlrepair,可按照命令修復資料表。


下面的名可用來更改 mysqlcheck 的默認行為:
mysqlrepair默認選項為 --repair
mysqlanalyze默認選項為 --analyze
mysqloptimize默認選項為 --optimize


mysqlcheck支持下面的選項:
-A, --all-databases
檢查所有數據庫中的所有資料表。與使用---database選項相同,在命令行中命名所有數據庫。
-a, --analyze
分析資料表。
-1, --all-in-1
不是為每個表發出一個語句,而是為命名數據庫中待處理的所有表的每個數據庫執行一個語句。
--auto-repair
如果某個被檢查的表破壞了,自動修復它。檢查完所有表後自動進行所有需要的修復。
--character-sets-dir=name
字符集的安裝目錄。參見5.10.1節,"數據和排序用字符集"。
-c, --check
檢查資料表的錯誤。
-C, --check-only-changed
只檢查上次檢查以來已經更改的或沒有正確關閉的資料表。
--compress
壓縮在客戶端和服務器之間發送的所有信息(如果二者均支持壓縮)。
-B, --databases
處理數據庫中命名的所有資料表。使用該選項,所有字名參量被看作數據庫名,而不是資料表名稱。
-#, --debug[=#]
寫調試日誌。 debug_options字符串通常為'd:t:o,file_name'。
--default-character-set=name
使用charsetas默認字符集。參見5.10.1節,"數據和排序用字符集"。
-F, --fast
只檢查沒有正確關閉的資料表。
-f, --force
即使出現SQL錯誤也繼續。
-e, --extended
如果你正使用該選項來檢查資料表,可以確保它們100%地一致,但需要很長的時間。 如果你正使用該選項來修復資料表,則運行擴展修復,不但執行的時間很長,而且還會產生大量的垃圾行!
-?, --help
顯示幫助消息並退出。
-h, --host=name
連接給定主機上的MySQL服務器。
-m, --medium-check
執行比--extended操作更快的檢查。只能發現99.99%的錯誤,在大多數情況下這已經足夠了。
-o, --optimize
優化資料表。
-p, --password[=name]
當連接服務器時使用的密碼。如果使用短選項形式(-p),選項和密碼之間不能有空格。 如果在命令行中--password或-p選項後面沒有密碼值,則提示輸入一個密碼。
-P, --port=#
用於連接的TCP/IP端口號。
--protocol=name
使用的連接協議。
-q, --quick
如果你正使用該選項在檢查資料表,它防止掃描行以檢查錯誤鏈接的檢查。這是最快的檢查方法。 如果你正使用該選項在修復資料表,它嘗試只修復索引樹。這是最快的修復方法。
-r, --repair
執行可以修復大部分問題的修復,只是唯一值不唯一時不能修復。
-s, --silent
沉默模式。只打印錯誤消息。
-S, --socket=name
用於連接的套接字文件。
--tables
覆蓋---database或-B選項。選項後面的所有參量被視為資料表名稱。
-u, --user=name
當連接服務器時使用的MySQL用戶名。
-v, --verbose
冗長模式。打印關於各階段程序操作的信息。
-V, --version
顯示版本信息並退出。

2013-06-04

[PHP] 分配上傳檔案的路徑

<?php

/*數字方式分配路徑*/
function allotPath($id, $extend='jpg') {
    $folders = str_split(sprintf("%012s", $id),3);
    $folders[3] = $id;

    return  '/'. join('/', $folders).'.'.$extend;
}

/*雜湊方式分配路徑*/
function allotHashPath($id, $extend='jpg') {
    $folders = array_slice( str_split(md5($id),2), 0, 4);
    $folders[] = $id;

    return  '/'. join('/', $folders).'.'.$extend;
}

var_dump(allotPath(122333));
// string(23) "/000/000/122/122333.jpg"

var_dump(allotHashPath(122333));
// string(23) "/9c/7c/c2/cd/122333.jpg"

[轉載][CSS] 3D 立體文字

轉載自:3D Text

3D 立體文字


h1.title_3d { 
    color: #FFFFFF;
    font: bold 50px/1 "Helvetica Neue",Helvetica,Arial,sans-serif;
        
    text-shadow: 
        0 1px 0 #ccc, 
        0 2px 0 #c9c9c9, 
        0 3px 0 #bbb, 
        0 4px 0 #b9b9b9,
        0 5px 0 #aaa, 
        0 6px 1px rgba(0,0,0,.1), 
        0 0 5px rgba(0,0,0,.1), 
        0 1px 3px rgba(0,0,0,.3), 
        0 3px 5px rgba(0,0,0,.2), 
        0 5px 10px rgba(0,0,0,.25), 
        0 10px 10px rgba(0,0,0,.2), 
        0 20px 20px rgba(0,0,0,.15); 
} 

[轉載] 有關程式的佳句

轉載自:忘記了
  • 上聯:插中有插常常插;下聯:一錯再錯都是錯;橫批:誰寫的爛程式!?
  • 防呆機制就是用你現在的五分鐘,換取以後你可能會用來補救的五十分鐘。
  • 就算開發效率再快,執行的結果不對的話就都是個屁。
  • 將功能切細,才能獲得組合的彈性。
  • 好的程式語言在設計上需要周密,在開發上需要簡化。
  • 有時愛情就像潛水一樣,愛得越深,壓力越大;沒毅力的人很快地就想要浮上水面喘口氣,執著的人卻是粉身碎骨葬身海底。
  • 有些系統就像森林裡的石頭一樣…一翻開來看底下都是蟲。
  • 所謂的大師,就是透徹某些技術的精髓,然後以最精簡的話語,來表達他的想法。
  • Reuse 就是 Copy 一行程式跟 Copy 十行程式的差別。
  • 高手包裝部份程式然後複製一行,新手複製整個程式然後修改一行。
  • 能傷害別人的武器,必定也能傷害自己。
  • 程式開發不成文定律:判斷式越多, Bug 也越多。
  • 少點抱怨就能多點專業。
  • 專業:就是從不在意不愉快的事開始,然後把心思放在該解決的事情上。
  • 只有當你透徹這項技術時,你才能坐在評審的位置。
  • 切入問題的角度,決定解決問題的速度。
  • 在 PHP 中,有時簡單的陣列,可以取代複雜的類別;簡單來說,就是不要捨近求遠。

[轉載] HTTP 狀態碼

轉載自:HTTP 狀態碼 - 網站管理員工具說明

當向您的伺服器傳送一個要求,要求顯示您網站的網頁 (例如,在使用者在瀏覽器中存取您的網頁,或 Googlebot 檢索該網頁時),則您的伺服器會傳回 HTTP 狀態碼,以回應該要求。

此狀態碼會提供有關該要求的狀態的資訊。此狀態碼會向 Googlebot 提供有關您的網站和所要求的網頁的資訊。

以下為一些常見的狀態碼:

  • 200 - 伺服器成功傳回網頁
  • 404 - 找不到所要求的網頁
  • 503 - 伺服器暫時無法使用

以下是 HTTP 狀態碼的完整清單。您還可以瀏覽有關 HTTP 狀態碼的 W3C 網頁,以取得更多資訊


1xx (Provisional response)
指出暫時回應並需要要求者採取措施以繼續的狀態碼。

  • 100 (Continue)
    要求者應當繼續此要求。伺服器傳回此代碼,指出已收到某個要求的第一部分,正等候其餘部分。
  • 101 (Switching protocols)
    要求者已請求伺服器切換通訊協定,伺服器正在確認即將進行切換。

2xx (Successful)
指出伺服器已成功處理該要求的狀態碼。

  • 200 (Successful)
    伺服器已成功處理該要求。一般而言,這代表伺服器已提供所要求的網頁。如果您在 robots.txt 檔案中看到此狀態,即表示 Googlebot 已成功擷取該網頁。
  • 201 (Created)
    該要求已成功完成,伺服器已建立新的資源。
  • 202 (Accepted)
    伺服器已接受該要求,但尚未處理。
  • 203 (Non-authoritative information)
    伺服器成功處理該要求,但正在傳回可能來自另一來源的資訊。
  • 204 (No Content)
    伺服器已成功處理該要求,但沒有傳回任何內容。
  • 205 (Reset Content)
    伺服器已成功處理該要求,但沒有傳回任何內容。與 204 回應不同,此回應需要要求者重設文件視圖 (例如,清除表單,以輸入新的資料)。
  • 206 (Partial Content)
    伺服器已成功處理部分 GET 要求。

3xx (Redirected)
您需要採取進一步的動作才能完成該要求。通常這些狀態碼會用來重新導向。Google 建議每個要求不要使用超過五次重新導向。您可以使用「網站管理員工具」來查看 Googlebot 在檢索重新導向的網頁時是否存在問題。在 [健康狀態] 下的「檢索錯誤」網頁會列出 Googlebot 因重新導向錯誤而無法檢索的網址。

  • 300 (Multiple Choices)
    根據該要求,伺服器可採取數種動作。伺服器可能會根據要求者 (user-agent) 選擇一個動作,或者可能列出清單供要求者選擇一個動作。
  • 301 (Moved Permanently)
    要求的網頁已永久移到新位置。當伺服器傳回此回應 (作為對 GET 或 HEAD 要求的回應) 時,會自動將要求者導向至新位置。您應該使用此代碼,讓 Googlebot 知道某個網頁或網站已永久移至新位置。
  • 302 (Moved Temporarily)
    伺服器目前正對來自不同位置的網頁回應該要求,但是要求者應該繼續使用原位置發出以後的要求。此代碼類似於回應 GET 或 HEAD 要求的 301 代碼,會自動將要求者導向至另一個位置,但是因為 Googlebot 會繼續檢索原位置並為其建立索引,所以您不應該使用此代碼來告知 Googlebot 某個網頁或網站已移除。
  • 303 (See Other Location)
    當要求者應該對另一個位置發出單獨的 GET 要求以擷取回應時,伺服器會傳回此代碼。對於 HEAD 之外的所有要求,伺服器會自動導向至其他位置。
  • 304 (Not Modified)
    要求的網頁自上次要求以後未經任何修改。當伺服器傳回此回應時,並不會傳回該網頁的內容。
    如果網頁自要求者上次要求以後,未經任何修改,則您應該設定伺服器傳回此回應 (稱為 If-Modified-Since HTTP 標頭)。這樣可以節省您的頻寬和負載,因為您的伺服器可以告知 Googlebot 網頁自上次檢索後,未經任何修改.
  • 305 (Use Proxy)
    要求者只能使用 Proxy 存取要求的網頁。當伺服器傳回此回應時,還會指出要求者應該使用的 Proxy。
  • 307 (Temporary Redirect)
    伺服器目前正對來自不同位置的網頁回應該要求,但是要求者應該繼續使用原位置發出以後的要求。此代碼類似於回應 GET 或 HEAD 要求的 301 代碼,會自動將要求者導向至另一個位置,但是因為 Googlebot 會繼續檢索原位置並為其建立索引,所以您不應該使用此代碼來告知 Googlebot 某個網頁或網站已移除。

4xx (Request Error)
這些狀態碼指出要求中可能存在使伺服器無法進行處理的錯誤。

  • 400 (Bad Request)
    伺服器無法解讀該要求的語法。
  • 401 (Not Authorized)
    該要求需要驗證。登入後,伺服器可能會對網頁傳回此回應。
  • 403 (Forbidden)
    伺服器拒絕要求。如果您看到 Googlebot 在嘗試檢索您網站的有效網頁時收到這個狀態碼 (您可以在 Google 網站管理員工具 [健康狀態] 標籤下的「檢索錯誤」網頁中看到這狀態碼),則可能是因為您的伺服器或主機封鎖了 Googlebot 的存取權。
  • 404 (Not found)
    伺服器找不到要求的網頁。例如,如果該要求是針對伺服器上不存在的網頁,則伺服器通常會傳回此代碼。
    如果您的網站上沒有 robots.txt 檔案,並在 Google 網站管理員工具的「封鎖的網址」網頁看到這個狀態,則這個狀態是正確的。不過,如果您有 robots.txt 檔案,並看到此狀態,則您的 robots.txt 檔案可能未正確命名或位於錯誤的位置。(應該位於網域的最高層級並且命名為 robots.txt)。
    如果您在 Googlebot 嘗試檢索的網址中看到這個狀態,則 Googlebot 所追蹤的很可能是來自其他網頁的無效連結 (可能是舊連結或拼字錯誤的連結)。
  • 405 (Method Not Allowed)
    不允許要求中指定的方法。
  • 406 (Not Acceptable)
    無法以所要求的內容特性回應要求的網頁。
  • 407 (Proxy Authentication Required)
    此狀態碼類似於 401(Not Authorized),但指定要求者必須使用 Proxy 進行驗證。當伺服器傳回此回應時,還會指出要求者應該使用的 Proxy。
  • 408 (Request Timeout)
    等候回應時,發生伺服器逾時。
  • 409 (Conflict)
    完成要求時,伺服器遇到衝突。伺服器必須包括有關在回應中的衝突的資訊。伺服器可能會在 PUT 要求與較早的某個要求相衝突時傳回此狀態碼作為回應,並會提供這兩個要求之間的差異清單。
  • 410 (Gone)
    要求的資源已永久移除後,伺服器會回傳此回應。此代碼類似於 404 (Not Found) 代碼,但在資源曾經存在但現已不復存在的情況下,有時會取代 404 而使用。如果資源已永久遷移,則應使用 301 指定資源的新位置。
  • 411 (Length Required)
    伺服器不接受不含有效內容長度不標頭欄位的要求。
  • 412 (Precondition Failed)
    伺服器未滿足要求者所要求的其中一個前提。
  • 413 (Request Entity Too Large)
    伺服器無法處理要求,因為要求過於龐大,伺服器無法處理。
  • 414 (Requested URI Is Too Long)
    要求的 URI (通常是網址) 過長,伺服器無法處理。
  • 415 (Unsupported Media Type)
    該要求的格式不受要求的網頁支援。
  • 416 (Requested Range Not Satisfiable)
    如果該要求是針對該網頁上無法使用的範則,則伺服器便會傳回此狀態碼。
  • 417 (Expectation Failed)
    伺服器無法滿足預期要求標頭欄位的要求。

5xx (Server Error)
這些狀態碼指出伺服器在嘗試處理要求時發生內部錯誤。這些錯誤可能是伺服器本身的錯誤,而不是要求的錯誤。

  • 500 (Internal Server Error)
    伺服器遇到錯誤,無法完成要求。
  • 501 (Not Implemented)
    伺服器不具備完成要求所需的功能。例如,當伺服器無法識別要求方式時,就會傳回此代碼。
  • 502 (Bad Gateway)
    伺服器用作閘道或 Proxy,接收到來自上游伺服器的無效回應。
  • 503 (Service unavailable)
    伺服器目前暫時無法使用 (因為超載或維護之故而關閉)。一般而言,這是暫時性狀態。
  • 504 (Gateway Timeout)
    伺服器用作閘道或 Proxy,而且未接收到來自上游伺服器的及時要求。
  • 505 (HTTP Version Not Supported)
    伺服器不支援要求中所用的 HTTP 通訊協定版本。

[轉載] 電腦開機(異常)BIOS嗶聲判斷

轉載自:電腦不開機 BIOS 嗶 嗶聲 說明教學

電腦開機時,會載入BIOS其電腦周邊相關硬體,而不同硬體周邊異常時,BIOS會利用機殼的喇叭發生嗶聲,利用不同的嗶聲可以判斷目前電腦的異常狀況為何。

但不同主機板,採用不同的BIOS公司與版本,則會有不一樣的聲音。

以下僅作為判斷參考,各電腦可參閱該主機板所提供的說明手冊判斷為準。


BIOS(CMOS)的異常聲音:

AMI BIOS
1短音DRAM復新失敗
1長音3短音音 DRAM錯誤
1長音8短音顯示測試失敗
2短音DRAM同位檢測失敗
3短音基本64K RAM測試失敗
4短音系統時鐘錯誤
5短音CPU處理器錯誤
6短音主機板鍵盤控制器錯誤
7短音CPU中斷錯誤
8短音顯示卡記憶體寫入/讀取錯誤
9短音ROM BIOS檢查碼錯誤
10短音CMOS 關機暫存器寫入/讀取錯誤
11短音快取記憶體故障

Award BIOS
1短音系統啟動正常
2短音CMOS設定錯誤
1長音1短音DRAM或主機板錯誤
1長音2短音顯示錯誤(顯示器或顯示卡)
1長音3短音鍵盤控制器錯誤
1長音9短音主機板Flash RAM或EPROM錯誤(BIOS損壞)
不斷地響(長音)DRAM沒插好或損壞
不斷地響電源,顯示器未和顯示卡連接好
重覆短響電源有問題

Phoenix
1短系統啟動正常
1短1短1短系統開機初始化失敗
1短1短2短主機板錯誤
1短1短3短CMOS或電池失效
1短1短4短ROM BIOS校驗錯誤
1短4短3短EISA時序器錯誤
1短3短1短RAM復新錯誤
3短1短2短主DMA暫存器錯誤
3短1短3短主中斷處理暫存器錯誤
3短2短4短主機板鍵盤控制器錯誤
3短4短2短顯示錯誤(顯示器或顯示卡)
4短2短2短關機錯誤
4短4短1短串列埠錯誤
4短4短2短並列埠錯誤

IBM BIOS
無畫面無聲電源供應器或主機板完全損壞
持續主機板嚴重損壞
短聲系統自我測試,情況正常
2短聲自我測試發現錯誤 ( 錯誤訊息顯示於螢幕上 )
持續短聲電源供應器或主機板損壞
1長1短主機板有錯誤
1長2短顯示卡錯誤
1長3短顯示卡錯誤
3長鍵盤錯誤

2013-05-30

[PHP] 從網頁執行 SVN 更新

想說寫一個透過網頁就可以執行 SVN 更新的程式,結果並不是我想得那樣簡單,有一些眉角需要注意的說。

先以 Apache 的使用者帳號執行 SVN checkout,這樣 Apache 才有 SVN 的連結權力,才可以透過網頁執行 SVN update

su -s /bin/bash www-data
cd /var/www
svn checkout http://www.xxx.com/svn/my_site 


  1. 在用 PHP 執行 shell 指令前要加上 export LANG=C.UTF-8 的環境宣告,不然 SVN update 時遇到中文會出現 error,Ubuntu 的 Apache 預設是 LANG=C
  2. 接著要為 SVN 補上 --accept theirs-full 的參數,這是當衝突發生時,都以 SVN Server 的檔案版本為主
  3. 最後再加上 2>&1,讓 PHP 可以取得包含錯誤的所有訊息
<?php
putenv('LANG=C.UTF-8');
$result = shell_exec('svn update --accept theirs-full /var/www/my_site 2>&1');
echo nl2br($result);

2013-05-29

[轉載] (X)HTML Strict 下的嵌套规则

轉載自:(X)HTML Strict 下的嵌套规则

下面是一份在 HTML 4 Strict 和 XHTML 1.0 Strict 下必须遵守的标签嵌套规则,比如你不能在 <a> 里面再嵌入一个 <a> 这样的约定。

说明:
  • 为了方便读者阅读,本文中的标签使用了大写(根据 XHTML 的规则,元素名必须小写,比如 <html> 而不应是 <HTML>
  • 小写的单词表明一组或一系列 HTML 标签
  • 每一项条目(标签)后都跟随一组标签列表,如果没有这个列表,那么表明该条目(标签)内部不允许包含任何标签。这意味着该条目内部只能包含纯文本内容(#PCDATA,见下文)。如果注明 (empty),这意味着该条目内部不允许包含任何形式的内容。对于 flow,inline,block,OBJECT 和 BODY,其内部允许包含的内容在文中会单独给出。
  • #PCDATA 的意思是“parsed character data”,即纯文本内容(不包括任何 HTML 标签,但是转义内容可以存在,比如 &auml;&#228;
  • CDATA 的意思是“character data”,这意味着不包括转义内容的纯文本内容,详细内容可以参考CDATA Confusion
  • excluding ... 意即不得直接或者间接的包含所列的元素



注1. 以上内容基于 [HTML 4.01 Specification] 的 Strict DTDJunChen 翻译自 Allowed nesting of elements in HTML 4 Strict (and XHTML 1.0 Strict)

注2. 对于 XHTML 1.0,基本上一致,不同点如下:
  • 对于 <script><style> 的内容,在 HTML 4 里是 CDATA 而在 XHTML 里是 #PCDATA
  • 在 XHTML 中,<table> 标签后可以紧跟一个 <tr>,而在 HTML 4.01 里,不允许这样,不过 <tbody> 标签又是可以省略的。意思就是说,如果代码中的 <table> 后紧跟 <tr>,对于 HTML 4.01,会隐性的生成一个 <tbody> 标签,而在 XHTML 里面就没有。这会影响到样式表使用 tbody 作为选择器。

[轉載] Default style sheet for HTML 4

轉載自:W3C Appendix D. Default style sheet for HTML 4

This appendix is informative, not normative.

This style sheet describes the typical formatting of all HTML 4 ([HTML4]) elements based on extensive research into current UA practice. Developers are encouraged to use it as a default style sheet in their implementations.

The full presentation of some HTML elements cannot be expressed in CSS 2.1, including replaced elements ("img", "object"), scripting elements ("script", "applet"), form control elements, and frame elements.

For other elements, the legacy presentation can be described in CSS but the solution removes the element. For example, the FONT element can be replaced by attaching CSS declarations to other elements (e.g., DIV). Likewise, legacy presentation of presentational attributes (e.g., the "border" attribute on TABLE) can be described in CSS, but the markup in the source document must be changed.

html, address,
blockquote,
body, dd, div,
dl, dt, fieldset, form,
frame, frameset,
h1, h2, h3, h4,
h5, h6, noframes,
ol, p, ul, center,
dir, hr, menu, pre   { display: block; unicode-bidi: embed; }

li              { display: list-item; }

head            { display: none; }

table           { display: table; }

tr              { display: table-row; }

thead           { display: table-header-group; }

tbody           { display: table-row-group; }

tfoot           { display: table-footer-group; }

col             { display: table-column; }

colgroup        { display: table-column-group; }

td, th          { display: table-cell; }

caption         { display: table-caption; }

th              { font-weight: bolder; text-align: center; }

caption         { text-align: center; }

body            { margin: 8px; }

h1              { font-size: 2em; margin: .67em 0; }

h2              { font-size: 1.5em; margin: .75em 0; }

h3              { font-size: 1.17em; margin: .83em 0; }

h4, p,
blockquote, ul,
fieldset, form,
ol, dl, dir,
menu            { margin: 1.12em 0; }

h5              { font-size: .83em; margin: 1.5em 0; }

h6              { font-size: .75em; margin: 1.67em 0; }

h1, h2, h3, h4,
h5, h6, b,
strong          { font-weight: bolder; }

blockquote      { margin-left: 40px; margin-right: 40px; }

i, cite, em,
var, address    { font-style: italic; }

pre, tt, code,
kbd, samp       { font-family: monospace; }

pre             { white-space: pre; }

button, textarea,
input, select   { display: inline-block; }

big             { font-size: 1.17em; }

small, sub, sup { font-size: .83em; }

sub             { vertical-align: sub; }

sup             { vertical-align: super; }

table           { border-spacing: 2px; }

thead, tbody,
tfoot           { vertical-align: middle; }

td, th, tr      { vertical-align: inherit; }

s, strike, del  { text-decoration: line-through; }

hr              { border: 1px inset; }

ol, ul, dir,
menu, dd        { margin-left: 40px; }

ol              { list-style-type: decimal; }

ol ul, ul ol,
ul ul, ol ol    { margin-top: 0; margin-bottom: 0; }

u, ins          { text-decoration: underline; }

br:before       { content: "\A"; white-space: pre-line; }

center          { text-align: center; }

:link, :visited { text-decoration: underline; }

:focus          { outline: thin dotted invert; }


/* Begin bidirectionality settings (do not change) */
BDO[DIR="ltr"]  { direction: ltr; unicode-bidi: bidi-override; }

BDO[DIR="rtl"]  { direction: rtl; unicode-bidi: bidi-override; }

*[DIR="ltr"]    { direction: ltr; unicode-bidi: embed; }

*[DIR="rtl"]    { direction: rtl; unicode-bidi: embed; }

@media print {
    h1            { page-break-before: always; }

    h1, h2, h3,
    h4, h5, h6    { page-break-after: avoid; }

    ul, ol, dl    { page-break-before: avoid; }
}

[轉載][PHP] Pattern Modifiers - 正規表示式的修飾符

下面是當前規則表達式裡可用的修飾. 括號內的名字是那些修飾符的內部 PCRE 名字.

i (PCRE_CASELESS)
如果設置了這個修飾符, 則表達式不區分大小寫.

m (PCRE_MULTILINE)
默認的, PCRE 認為目標字符串值是單行字符串 (即使他確實包含多行). 行開始標記 (^) 只匹配字符串的開始部分, 而行結束標記 ($) 只匹配字符串的尾部,或者一個結束行(除非指定 E 修飾符). 這個和 Perl 裡面一樣.

如果設定了這個修飾符, 行開始和行結束結構分別匹配在目標字符串任何新行的當前位置後面的或者以前的, 和每一個開始和結束一樣. 這個等於 Perl 裡面的 /m 修飾符. 如果目標字符串沒有 "n" 字符, 或者模式裡沒有 ^ 或 $ ,這個修飾符不起作用.

s (PCRE_DOTALL)
如果設置這個修飾符, 模式裡的一個"點"將匹配所有字符, 包括換行. 沒有他, 換行將被排除在外. 這個修飾符等同於 Perl 裡面的 /s 修飾符. 一個相反的類型,例如 [^a] 將總是匹配換行字符,而不管這個修飾符的限制.

x (PCRE_EXTENDED)
如果設置這個修飾符, 模式裡面的空格數句將會被全部忽略,除非用轉義符或者一個字符的內部類型,還有所有字符類型外的未轉義的 # 號之間的也被忽略. 這個等同於 Perl 裡面的 /x 修飾符, 這樣可以複雜的模式裡面加入註釋. 注意,只適用於數據字符. 空格字符將不會在指定的模式字符指定順序中出現。

e
如果設置這個修飾符, preg_replace() 將在替換值裡進行正常的涉及到 \ 的替換, 等同於在 PHP 代碼裡面一樣, 然後用於替換搜索到的字符串.

只在 preg_replace() 裡使用這個修飾符; 其它 PCRE 函數忽略他.

A (PCRE_ANCHORED)
如果設置這個修飾符, 模式被強制為錨(anchored), 也就是說, 他將值匹配搜索字符串的開始. 這個效果可以通過恰當的模式結構自身來實現,那是在 Perl 裡面的唯一途徑.

D (PCRE_DOLLAR_ENDONLY)
如果設置這個修飾符,則模式裡的 $ 修飾符將僅匹配目標字符串裡的尾部. 沒有這個修飾符, $ 字符也匹配新行的尾部 (但是不再新行的前面). 如果設置了 m 修飾符則忽略這個修飾符. 在 Perl 裡面沒有類似的.

S
如果一個模式將被使用多次, 使用長些時間分析他來來提高匹配的速度. 如果使用這個修飾符,則進行額外的分析. 目前, 研究模式僅用於非錨模式,沒有一個固定的開始字符.

U (PCRE_UNGREEDY)
這個修飾符翻轉數量的 "greediness" ,使得默認不被 greedy,但是如果你緊跟問號(?),則可以 greedy. 這個和 Perl 不兼容. 這個也可以通過在模式裡面的(?U) 修飾符得到.

X (PCRE_EXTRA)
這個修飾符打開額外的功能,這些和 Perl 不兼容. 任何模式裡面的後面帶字符但沒有特殊意義的反斜槓將引起錯誤, 從而儲備這些聯合用於將來的擴充. 默認的, 在 Perl 裡面, 反斜槓後面有無意義的字符被當成正常的 literal. 目前還沒有其他的控制特徵

[轉載][Ubuntu] 檔案系統格式簡介

發信人: tdb.bbs@ptt.cc (tbd), 看板: Linux
標 題: [分享] 檔案系統格式簡介
發信站: 批踢踢實業 (Wed Jul 19 06:22:33 2006)
轉信站: SayYa!ctu-reader!news.nctu!ptt
Origin: sally.csie.ntu.edu.tw

在 MS Winsows 的世界,硬碟可以格式化成 NTFS、FAT32、FAT16 等等。相同的,在 GNU/Linux 底下也是有很多不同的檔案系統格式可以選擇喔。目前在 GNU/Linux 底下,比較常用的有以下這幾種格式 Ext3, ReiserFS, XFS 和 JFS 等數種。當然各種格式都有其憂缺點,所以我們將在下面給與簡單的介紹。

除了 Ext2 以外,其它幾種都是日誌型檔案系統。那什們是日誌系統呢?就是系統會多用一些額外的空間紀錄硬碟的資料狀態,因而在不正常開關機後,不需整個硬碟從新掃描,來恢復系統狀態。


Ext2
此為一非常老舊且不支援日誌系統的檔案系統格式,早期 Linux 玩家還記得吧,每次不正常關機後,重新開機時錯誤檢查很久,且在沒有正常關機下,常常會讓您一次不見很多檔案,現在很少人使用這款檔案系統了!

Ext3
為 Ext2 個改良版,所以 Ext2 可以直接升級成為 Ext3 而不必從新格式化,這也可以讓舊的 Ext2 系統更加穩定。而主要和 Ext2 的差別是,增加了日誌系統 (metadata),所以在不正常開關機時,可以迅速使系統恢復。而因為它舊有的系統相容,和所以很多發行版預設使用 Ext3。而在實際測試上,它的硬碟使用率不佳,大概只有真正空間的 93%會被使用到,並且其它效能測試表現中等。而且它在格式化與建立檔案系統的時間也是其它種類的數十倍。

ReiserFS - http://www.namesys.com
它是採用日誌型系統,為 Hans Reiser 所創使,所以以他的名子命名。技術上使用的是 B*-tree 為基礎的檔案系統,其特色為能很有效率地處理大型檔案到眾多小檔案都可以用很高的效率處理。實務上 ReiserFS 在處理檔案小於 1k 小檔案時,甚至效率可以比 Ext3 快約 10 倍,所以 ReiserFS 專長在處理很多小檔案。而在一般操作上,它的效能表現也有中上的程度。

XFS - http://oss.sgi.com/projects/xfs/
為繪圖工作站公司 SGI 為了高級繪圖處理器系統 IRIX 所設計的檔案格式,也是日誌型系統。而 SGI 亦將其移植到 GNU/Linux 上。而他本來是針對高效能繪圖設計,且為高階工作站使用,所以他在穩定行和效率是無雍致疑的。而在實務上的表現,它的處理各種檔案大小混合的情況下效率最好,並且在一般使用上有不錯的表現。

JFS - http://jfs.sourceforge.net
為全球最大電腦供應商 IBM 為 AIX 系列設計的日誌型檔案系統,技術上使用的是 B+-tree 為基礎的檔案系統,和 ReiserFS 使用 B*-tree 不同。而在穩定度上,IBM AIX 伺服器使用它,而此系列機器很多都在金融上使用,所以穩定是沒話說的。而它最重要的特色是在處理檔案 I/O 的時候,是這些檔案系統裏面最不佔 CPU 資源的,也就是 CPU 使用率最低。但在這樣節省使用 CPU 的情況下,它的效率表現也有中上以上的程度。


雖然 Ext3 效能不好 (在日誌型檔案系統中效率上最糟糕的),那為何那們多人使用?那是因為當時 Ext3 可以直接從 Ext2 升級,而不需要先備份然後格式化後再把檔案拷備回去,所以造成使用人數較多了。但這也是不能怪它,因為它為了和 Ext2 相容,所以有很多的歷史包袱存在。因此我建議新的電腦考慮使用 ReiserFX,XFS 或 JFS。若是以效能為考慮,則可以選擇 ReiserFS 或 XFS。若是系統資源不多,要使用最低的 CPU 使用率,那們可以選擇 JFS,它有著最好的效能資源比。

而網路上有一些檔案系統效能評估,這裡列出來給讀者參考一下

http://www.debian-administration.org/articles/388
http://fsbench.netnation.com/
http://linuxgazette.net/122/TWDT.html
http://linuxgazette.net/102/piszcz.html

2013-05-20

[PHP] aHash, pHash, dHash 實做

<?php
/**
 * 圖片特徵 Hash 計算
 *
 * @version     $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax $
 * @author      jax.hu
 *
 * <code>
 *  //Sample_1
 *  $hashA = ImageHash::pHash('001.jpg');
 *  $hashB = ImageHash::pHash('002.jpg');
 *  if(ImageHash::isSimilar($hashA, $hashB)){
 *
 *  }
 *
 * </code>
 */

class ImageHash {

    /**讀取圖片至指定大小*/
    public static function readImageTo($imagePath, $width, $height){
        if(!$imagePath || !file_exists($imagePath)){ return null; }

        if(class_exists('Imagick')){
            $image = new Imagick($imagePath);
            $image->thumbnailImage($width, $height);
            $img = imagecreatefromstring($image->getImageBlob());
            $image->destroy(); $image = null;

        }else{
            $createFunc = array(
                IMAGETYPE_GIF   =>'imageCreateFromGIF',
                IMAGETYPE_JPEG  =>'imageCreateFromJPEG',
                IMAGETYPE_PNG   =>'imageCreateFromPNG',
                IMAGETYPE_BMP   =>'imageCreateFromBMP',
                IMAGETYPE_WBMP  =>'imageCreateFromWBMP',
                IMAGETYPE_XBM   =>'imageCreateFromXBM',
            );

            $type = exif_imagetype($imagePath);
            if(!array_key_exists($type, $createFunc)){ return null; }

            $func = $createFunc[$type];
            if(!function_exists($func)){ return null; }

            $src = $func($imagePath);
            $img = imageCreateTrueColor($width, $height);
            imageCopyResized(
                $img, $src, 
                0,0,0,0, 
                $width, $height, imagesX($src),imagesY($src)
            );
            imagedestroy($src);
        }

        return $img;
    }



    /**取得灰階數值*/
    public static function getGray($img,$x,$y){
        $col = imagecolorsforindex($img, imagecolorat($img,$x,$y));
        return intval($col['red']*0.3 + $col['green']*0.59 + $col['blue']*0.11);
    }



    /**取得 DCT 常數*/
    private static $_dctConst = null;
    public static function getDctConst(){
        if(self::$_dctConst){ return self::$_dctConst;}

        self::$_dctConst = array();
        for ($dctP=0; $dctP<8; $dctP++) {
            for ($p=0;$p<32;$p++) {
                self::$_dctConst[$dctP][$p] = 
                    cos( ((2*$p + 1)/64) * $dctP * pi() );
            }
        }

        return self::$_dctConst;
    }



    /**圖片檔案 aHash
     * @param string $filePath 檔案位址路徑
     * @return string 圖片 hash 值,失敗則是 null
     * */
    public static function aHash($imagePath){
        $img = self::readImageTo($imagePath, 8, 8);
        if(!$img){ return null; }

        $graySum = 0;
        $grays = array();
        for ($y=0; $y<8; $y++){
            for ($x=0; $x<8; $x++){
                $gray = self::getGray($img,$x,$y);
                $grays[] = $gray;
                $graySum +=  $gray;
            }
        }
        imagedestroy($img);

        /*計算所有像素的灰階平均值*/
        $average = $graySum/64;

        /*計算 hash 值*/
        foreach ($grays as $i => $gray){
            $grays[$i] = ($gray>=$average) ? '1' : '0';
        }

        return join('',$grays);
    }




    /**圖片檔案 pHash
     * @param string $filePath 檔案位址路徑
     * @return string 圖片 hash 值,失敗則是 null
     * */
    public static function pHash($imagePath){
        $img = self::readImageTo($imagePath, 32, 32);
        if(!$img){ return null; }

        /*取得灰階數值 32*32*/
        $grays = array();
        for ($y=0; $y<32; $y++){
            for ($x=0; $x<32; $x++){
                $grays[$y][$x] = self::getGray($img,$x,$y);
            }
        }
        imagedestroy($img);


        /*計算 DCT 8*8*/
        $dctConst = self::getDctConst();
        $dctSum = 0;
        $dcts = array();
        for ($dctY=0; $dctY<8; $dctY++) {
            for ($dctX=0; $dctX<8; $dctX++) {

                $sum = 1;
                for ($y=0;$y<32;$y++) {
                    for ($x=0;$x<32;$x++) {
                        $sum += 
                            $dctConst[$dctY][$y] * 
                            $dctConst[$dctX][$x] * 
                            $grays[$y][$x];
                    }
                }

                /*apply coefficients*/
                $sum *= .25;
                if ($dctY == 0 || $dctX == 0) {
                    $sum *= 1/sqrt(2);
                }

                $dcts[] = $sum;
                $dctSum +=  $sum;
            }
        }

        /*計算所有像素的灰階平均值*/
        $average = $dctSum/64;

        /*計算 hash 值*/
        foreach ($dcts as $i => $dct){
            $dcts[$i] = ($dct>=$average) ? '1' : '0';
        }

        return join('',$dcts);
    }



    /**圖片檔案 dHash
     * @param string $filePath 檔案位址路徑
     * @return string 圖片 hash 值,失敗則是 null
     * */
    public static function dHash($imagePath){
        $img = self::readImageTo($imagePath, 9, 8);
        if(!$img){ return null; }

        $grays = array();
        for ($y=0; $y<8; $y++){
            for ($x=0; $x<9; $x++){
                $grays[$y][$x] = $gray = self::getGray($img,$x,$y);
            }
        }
        imagedestroy($img);

        $bitStr = array();
        for ($y=0; $y<8; $y++){
            for ($x=0; $x<8; $x++){
                $bitStr[] = ($grays[$y][$x] < $grays[$y][$x+1]) ? '1' : '0';
            }
        }

        return join('',$bitStr);
    }



   /**比較兩個 hash 值,是不是相似
    * @param string $aHash A圖片的 hash 值
    * @param string $bHash B圖片的 hash 值
    * @return bool 當圖片相似則回傳 true,否則是 false
    * */
   public static function isSimilar($hashStrA, $hashStrB){
       $aL = strlen($hashStrA); $bL = strlen($hashStrB);
       if ($aL !== $bL){ return false; }

       /*計算兩個 hash 值的漢明距離*/
       $distance = 0;
       for($i=0; $i<$aL; $i++){
           if ($hashStrA{$i} !== $hashStrB{$i}){ $distance++; }
       }

       return ($distance <= 10) ? true : false;
   }

}

2013-05-01

[C#] 利用 LINQ 解析 XML 至 POCO

menu_config.xml
<?xml version="1.0" encoding="utf-8"?>
  <menu_config>
    <menu title="文章管理" url="~/Article" target="" allow="">
        <submenu title="列表" url="~/Article/list" allow="" />
        <submenu />
        <submenu title="新增" url="~/Article/add" allow="" />
    </menu>
    <menu />
    <menu title="帳號管理" url="~/Admin" />
  </menu_config>


//using System.Collections.Generic;
//using System.IO;
//using System.Linq;
//using System.Xml.Linq;
//using System.Text;

public class MenuDataModel
{
    /*POCO 資料欄位*/
    public string Title { get; set; }
    public string Url { get; set; }
    public string Target { get; set; }
    public string Allow { get; set; }
    public string Icon { get; set; }
    public List<MenuDataModel> Submenu { get; set; }

    /*取得資料清單*/
    public static List<MenuDataModel> GetList(string menuConfigPath)
    {
        /*讀取 XML 檔案*/
        var xmlContent = File.ReadAllText(menuConfigPath, Encoding.UTF8);
        var menuDocument = XDocument.Parse(xmlContent);

        /*利用 LINQ 轉成 POCO*/
        return menuDocument.Root.Elements("menu")
        .Select(menu => new MenuDataModel
        {
            /*取得 Element 上的 Attribute*/
            Title   = (string) menu.Attribute("title"),
            Url     = (string) menu.Attribute("url"),
            Target  = (string) menu.Attribute("target"),
            Icon    = (string) menu.Attribute("icon") ?? "Item",
            Allow   = (string) menu.Attribute("allow"),

            /*取得子層級 Element 上的 Attribute*/
            Submenu = menu.Elements("submenu")
            .Select(sub => new MenuDataModel
            {
                Title   = (string) sub.Attribute("title"),
                Url     = (string) sub.Attribute("url"),
                Target  = (string) sub.Attribute("target"),
                Icon    = (string) sub.Attribute("icon") ?? "Item",
                Allow   = (string) sub.Attribute("allow"),
            }).ToList(),
        }).ToList();
    }
}


void Main()
{
    var path = @"D:\menu_config.xml";

    List<MenuDataModel> list =  MenuDataModel.GetList(path);

    list.Dump();
}

[C#] 取得 URL 頁面上的 title 內容

//using System.Net;
//using System.IO;
//using System.Text;

string url = @"http://msdn.microsoft.com/en-us/library/az24scfc.aspx";
string title = String.Empty;

WebResponse response = null;
WebRequest request = WebRequest.Create(url);

/*設定最長執行的毫秒數*/
request.Timeout = 10000; 

try{
    /*取得 URL 頁面資料*/
    response = request.GetResponse();
    StreamReader stream = new StreamReader(
        response.GetResponseStream(), Encoding.UTF8
    );

    /*只取得前 4096 個字*/
    char[] buf = new char[4096];
    stream.Read(buf, 0, buf.Length);

    /*尋找標題字串*/
    string pageText = new String(buf);
    string pattern = @"(?<=<title[^>]*>)([^<]*)(?=</title>)";
    title = Regex.Match(pageText, pattern, RegexOptions.IgnoreCase)
            .Value.Trim();

}catch(WebException e){
}finally{
    if(response!=null){ response.Close(); }
}

title.Dump();

2013-04-04

PDO fetch 模式的回傳結果

PDO 的 fetch 模式功能實在是太方便了,但每次要產生想要的結果都要試太麻煩了,這裡列出可能的組合。

<?php
$dbAdapter = new PDO("mysql:host=localhost;dbname=test", "root", "1234");
$dbAdapter->exec("SET NAMES 'utf8';");




$data = $dbAdapter->query("
    SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_ASSOC);

//var_dump($data);
/*
array(
    array(
        'id' => '1',
        'name' => 'HBO',
        'method' => 'service',
    ),
    array(
        'id' => '2',
        'name' => '本週新片',
        'method' => 'movie',
    ),
    array(
        'id' => '3',
        'name' => '熱映中',
        'method' => 'movie',
    ),
)
*/



$data = $dbAdapter->query("
    SELECT name, method FROM category
")->fetchAll(PDO::FETCH_COLUMN);

//var_dump($data);
/*
array(
    'HBO',
    '本週新片',
    '熱映中',
)
*/



$data = $dbAdapter->query("
    SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC);

//var_dump($data);
/*
array(
    '1' => array(
        'name' => 'HBO',
        'method' => 'service',
    ),
    '2' => array(
        'name' => '本週新片',
        'method' => 'movie',
    ),
    '3' => array(
        'name' => '熱映中',
        'method' => 'movie',
    ),
)
*/



$data = $dbAdapter->query("
    SELECT method, id, name FROM category
")->fetchAll(PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC);

//var_dump($data);
/*
array(
    'service' => array(
        'id' => '1',
        'name' => 'HBO',
    ),
    'movie' => array(
        'id' => '3',
        'name' => '熱映中',
    ),
)
*/



$data = $dbAdapter->query("
    SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_UNIQUE | PDO::FETCH_COLUMN);

//var_dump($data);
/*
array(
    '1' => 'HBO',
    '2' => '本週新片',
    '3' => '熱映中',
)
*/



$data = $dbAdapter->query("
    SELECT method, name, id FROM category
")->fetchAll(PDO::FETCH_UNIQUE | PDO::FETCH_COLUMN);

//var_dump($data);
/*
array(
    'service' => 'HBO',
    'movie' => '熱映中',
)
*/




$data = $dbAdapter->query("
    SELECT method, id, name FROM category
")->fetchAll( PDO::FETCH_ASSOC | PDO::FETCH_GROUP);

//var_dump($data);
/*
array(
    'service' => array(
        array(
            'id' => '1'
            'name' => 'HBO'
        ),
    )
    'movie' => array(
        array(
          'id' => '2'
          'name' => '本週新片'
        ),
        array(
          'id' => '3'
          'name' => '熱映中'
        ),
    )
)
*/




$data = $dbAdapter->query("
    SELECT method, name, id FROM category
")->fetchAll(PDO::FETCH_GROUP | PDO::FETCH_COLUMN);

//var_dump($data);
/*
array(
    'service' => array(
        'HBO'
    ),
    'movie' => array(
        '本週新片'
        '熱映中'
    ),
)
*/





$data = $dbAdapter->query("
    SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_OBJ);

//var_dump($data);
/*
array(
    stdClass{
        public $id = '1';
        public $name = 'HBO';
        public $method = 'service';
    },
    stdClass{
        public $id = '2';
        public $name = '本週新片';
        public $method = 'movie';
    },
    stdClass{
        public $id = '3';
        public $name = '熱映中';
        public $method = 'movie';
    },
)
*/







class Category_1 {}

$data = $dbAdapter->query("
    SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Category_1");

//var_dump($data);
/*
array(
    Category_1{
        public $id = '1';
        public $name = 'HBO';
        public $method = 'service';
    },
    Category_1{
        public $id = '2';
        public $name = '本週新片';
        public $method = 'movie';
    },
    Category_1{
        public $id = '3';
        public $name = '熱映中';
        public $method = 'movie';
    },
),
*/





class Category_2 {
    public $name;
    public $method;

    public function __construct() {}
    public function __set($name, $value ){}
}

$data = $dbAdapter->query("
    SELECT id, name, method FROM category
")->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Category_2");

//var_dump($data);
/*
array(
    Category_2{
        public $name = 'HBO';
        public $method = 'service';
    },
    Category_2{
        public $name = '本週新片';
        public $method = 'movie';
    },
    Category_2{
        public $name = '熱映中';
        public $method = 'movie';
    },
)
*/

2013-02-26

[JSX] 串接圖片

由於工作上的需要將圖片分類串接成好幾組圖片,所以寫了一個 PhotoShop Scripting 來完成這件事。

圖片名稱是:
a001-01.jpg
a001-02.jpg
a002-01.jpg
a002-02.jpg
b001-01.jpg
b001-02.jpg
b001-03.jpg

而希望可以依 a001.jpg, a002.jpg, b001.jpg 分類串接在一起。

執行方式:
  1. 檔案 -> 指令碼 -> 瀏覽 -> 選擇 photo_join.jsx
  2. 在選擇多張要串接的圖片

/** 圖片串接 for (PhotoShop Script)
 *  author : Jax
 *  email : weskerjax[at]gmail.com
 *  website : http://jax-work-archive.blogspot.com/
 *  history : 2013.02.23
 */
// photo_join.jsx
//#target photoshop


/**水平邊界px*/
var levMargin = 10;

/**垂直邊界px*/
var verMargin = 10;

var WebJpegOptions = new ExportOptionsSaveForWeb()
WebJpegOptions.format = SaveDocumentType.JPEG;
WebJpegOptions.quality=80;

function main(){
    /*取得多個的檔案路徑*/
    var fileList = File.openDialog(
        "開啟 JPG 檔案",
        "JPG Files:*.jpg",
        true
    );
    if(fileList.length == 0){ alert("沒有任何檔案"); return; }

    /*依檔名排序*/
    fileList.sort();

    /*檔案依前綴分群*/
    var groupList = {};
    var filePath, i=0;
    while(filePath=fileList[i++]){
        var prefix=filePath.name.match(/([a-z0-9]+)[-_].+\.jpg/i);
        if(!prefix || !prefix[1]){continue;}
        if(!groupList[prefix[1]]){ groupList[prefix[1]]=[]; }
        groupList[prefix[1]].push(filePath);
    }

    /*依分群串接圖片*/
    for(prefix in groupList){
        photoJoin(prefix,groupList[prefix]);
    }
}


function copyArt(toArt, fromArt){
    /*複製圖層*/
    app.activeDocument=fromArt;
    fromArt.selection.selectAll()
    fromArt.selection.copy()

    /*貼上圖層*/
    app.activeDocument=toArt;
    return toArt.paste();
}


function photoJoin(prefix, fileList){
    /*新增目標圖片文件*/
    var newPic = app.documents.add(
        1, 1, 72,
        prefix,
        NewDocumentMode.RGB,
        DocumentFill.TRANSPARENT
    );

    workSpace=fileList[0].parent.fsName.replace(/\\/ig,'/');;

    var height=0;
    var width = newPic.width;
    var correspond = [];

    /*複製所有圖檔至新建立的圖檔*/
    for (var i=0; i<fileList.length; i++){
        try {
            var atDoc=open(new File(fileList[i]));
        } catch (e) { continue; }

        /*累計高度*/
        height += atDoc.height.value + verMargin;
        /*最大寬度*/
        width = Math.max(width,atDoc.width);

        /*複製圖層*/
        var layer = copyArt(newPic, atDoc);
        correspond.push({
            'layer':layer,
            'height':atDoc.height.value
        });

        /*關閉檔案*/
        atDoc.close(SaveOptions.DONOTSAVECHANGES);
    };

    app.activeDocument=newPic;

    /*變更圖片大小*/
    newPic.resizeCanvas(
        width+levMargin*2,
        height+verMargin,
        AnchorPosition.TOPLEFT
    );

    /*變更圖層定位*/
    var top=verMargin;
    for (var i=0,l=correspond.length; i<l; i++){
        if(correspond[i].layer===undefined){continue;}

        correspond[i].layer.translate(levMargin, top); /*移動圖層*/
        top += correspond[i].height+verMargin; /*定位累計*/
    };

    /*匯出網頁JPEG*/
    newPic.exportDocument(
        new File(File.encode(workSpace+"/"+prefix+".jpg")),
        ExportType.SAVEFORWEB,
        WebJpegOptions
    );

    /*關閉 newArt 文件*/
    newPic.close(SaveOptions.DONOTSAVECHANGES);
}

/*把Photoshop推到最上層*/
app.bringToFront();
/*設定使用的單位為「像素(Pixel)」*/
app.preferences.rulerUnits = Units.PIXELS;
/*執行主程式*/
main();

alert('成功執行!');