亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
123下一頁
最近訪問板塊 發(fā)新帖
查看: 25901 | 回復(fù): 24
打印 上一主題 下一主題

[學(xué)習(xí)]第一個(gè) Ruby 程序 - 簡(jiǎn)易 Log 分析器 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2007-08-14 16:39 |只看該作者 |倒序?yàn)g覽
謹(jǐn)寫此篇希望為 Ruby 和 CU Ruby 版的發(fā)展盡一份薄力。因?yàn)?Ruby 真的是很優(yōu)雅、很強(qiáng)大而又很靈活的語言。

前言

開始知道 Ruby 是什么時(shí)候已經(jīng)忘了,但是確實(shí)一直深深被其所吸引。用 PHP 差不多 3 年了,卻一直不喜歡它過于隨意的方式,最不喜歡的是把所有函數(shù)都放在全局空間,名字和函數(shù)位置也很讓人詬病。但是一直沒有一個(gè)特別的需求一定要學(xué) Ruby,所以也就隨意看了些 Ruby 的教程,后來看到 RoR,很受啟發(fā),用 PHP 5 的新功能寫了一個(gè)類似的小框架。但還是沒有真正學(xué) Ruby,只覺得,哦,方便,哦,清楚,哦,不錯(cuò),就完了。

終于,這一天到來了。這次學(xué)習(xí) System Adminstration,第一個(gè)作業(yè),寫一個(gè)分析訪問記錄(log)的小程序,要求,用腳本(當(dāng)然你想用 C++ 沒人攔你)。而作業(yè)要求明明寫到,Python/Perl/Ruby。Python 最近也在看,因?yàn)?Ruby 太常拿來給 Python 比較了。但是就 Python 來說,OO 得不夠徹底,雖然在很多情況下更為清晰(比如 list comprehension),但是對(duì)于我這種完美主義者來說,語言的 consistency 比較重要,所以沒有更深地接觸 Python。Perl 就不說了,就我個(gè)人來說不愿意花太多時(shí)間去記一門語言的語法和關(guān)鍵詞等等,因?yàn)橐呀?jīng)要花很多時(shí)間來記 Unix 和 Emacs 的命令什么的了。只想輕松一點(diǎn)。

后來去問講師,可以用 PHP 么?我其實(shí)覺得應(yīng)該是可以的,畢竟都是 Scripting Language,某種意義上。但是他明確回答不行。自然最后就只有 Ruby 了。

開始

我先說明:我還算比較了解 PHP 關(guān)于中小型網(wǎng)站開發(fā)的部分,PHP 5 也有接觸。另外 C++/Java 都會(huì)一些;關(guān)于 Ruby 的資料看得不多也不系統(tǒng),很多時(shí)候就是蜻蜓點(diǎn)水類地泛泛而看。

開發(fā)環(huán)境

這是一個(gè)很小的程序,所以不太需要太強(qiáng)大的 IDE,EditPlus 足夠了,但是我想要更融合 Ruby 的編輯器,所以搜索了之后選定 RDE,很簡(jiǎn)單,也很小。下載 RDE 之后,你需要選擇 Ruby 的位置,然后即可進(jìn)入。

作業(yè)

這次的作業(yè)也很簡(jiǎn)單,要求是:

  1. 1183245989.174 0.129 137.157.56.174 200 1277 GET http://linux.pacific.net.au/linux/packman/suse/10.2/repodata/repomd.xml "text/xml"
  2. 1183245989.254 0.059 137.157.56.174 200 1277 GET http://linux.pacific.net.au/linux/packman/suse/10.2/repodata/repomd.xml "text/xml"
  3. ...
復(fù)制代碼


分析類似于上面這種 log 文件。需要的東西是:

C3 = User IP (Identification of User)
C4 = HTTP Status (200 for success)
C5 = File size sent
C6 = HTTP Request
C7 = Host (extract from URL)
C8 = MIME quoted in doublequotes

程序命令為:

logwhacker [-u ip-address] [-s] [-b] [-t] logfile

-u 為用戶檢查模式,輸出 IP 為 ip-address 的用戶有

- 多少行記錄
- 多少 GET
- 多少成功的 GET
- 下載了多少 Byte
- 最常去的域名/主機(jī)名
- 最常下載的 MIME

-s 為報(bào)告模式,輸出

- 總共多少行記錄
- 總共傳送多少 Byte
- 每個(gè)用戶分別使用多少 Byte
- 總共多少成功的 GET
- 訪問量前 10 位主機(jī)名
- 訪問量前 10 位 MIME

-b 為賬單模式,輸出

- 國(guó)際 URL 的訪問量 (主機(jī)名不是以 .au 結(jié)尾)
- 本地 URL 的訪問量
- 總花費(fèi) ($) 單價(jià)為:國(guó)際 $1024/MB,本地 $4096/MB

-u -s -b 為三選一

-t 為輸出 CPU 時(shí)間,輸出

- 平均處理每行使用時(shí)間

-t 可有可無。

寫代碼

首先,先處理一行。也就是:

  1. data = '1183245989.174 0.129 137.157.56.174 200 1277 GET http://linux.pacific.net.au/linux/packman/suse/10.2/repodata/repomd.xml "text/xml"'
復(fù)制代碼


要把這些資料提取出來,用正則(因?yàn)闀簳r(shí)還不知道其它辦法,正則比較簡(jiǎn)單一些)。觀察資料后發(fā)現(xiàn)所有資料都是由空格擱開,資料之間沒有空格,所以:

  1. data = '1183245989.174 0.129 137.157.56.174 200 1277 GET http://linux.pacific.net.au/linux/packman/suse/10.2/repodata/repomd.xml "text/xml"'

  2. data = data.split(/\s/)
復(fù)制代碼


注意 Ruby 不需要分號(hào)結(jié)尾。斜線(//)和引號(hào)一樣,作為指定范圍的工具,不過斜線指定的是 RegExp,Ruby 的規(guī)則表達(dá)式類。split 是 String 類的一個(gè)方法,分割這個(gè)字符串。

現(xiàn)在,data 就是一個(gè) Array 了,裝著分割好的字符串,不用管類型,這個(gè)和 PHP 以及很多腳本語言一樣。

按前面的要求,我們需要的是:

C3 = User IP (Identification of User)
C4 = HTTP Status (200 for success)
C5 = File size sent
C6 = HTTP Request
C7 = URL
C8 = MIME quoted in doublequotes

所以:

  1. data = '1183245989.174 0.129 137.157.56.174 200 1277 GET http://linux.pacific.net.au/linux/packman/suse/10.2/repodata/repomd.xml "text/xml"'

  2. data = data.split(/\s/)

  3. columns = Hash.new
  4. columns[:user_ip] = data[2]
  5. columns[:http_status] = data[3]
  6. columns[:file_size] = data[4]
  7. columns[:http_request] = data[5]
  8. columns[:host] = data[6].match(/^http:\/\/([^\/]+)/)[1] # thanks for DennisRitchie
  9. columns[:mime] = data[7]
復(fù)制代碼


首先,建立了一個(gè) Hash。我暫時(shí)還不知道 columns 是不是必須先賦值 Hash.new,才能用 [],不過保險(xiǎn)起見,先這樣搞。答案是:是的。(感謝 DennisRitchie 指出)[]= 和 [] 都是 Hash (以及 Array)的方法,所以變量必須先是 Hash (Array) 才能使用這兩個(gè)方法。Hash.new 的簡(jiǎn)便方式是 {}。

然后是可能會(huì)讓 PHPer 奇怪的冒號(hào)。冒號(hào)開頭代表 Ruby 的 Symbol 類實(shí)例。何謂 Symbol 類?其實(shí)和 String 是差不多的,但是在一般語言里面即使兩個(gè) String 的內(nèi)容完全一樣,也會(huì)分配不同的內(nèi)存,這一分一放,終究非常浪費(fèi),所以 Ruby 有了 Symbol,Smalltalk 也有 Symbol,而 Java 也有 String interning,專門解決這個(gè)問題。Symbol 再多只要內(nèi)容一樣,也只會(huì)分配一次內(nèi)存。這也讓這次作業(yè)這種平行儲(chǔ)存結(jié)構(gòu)更加有效。

那么 PHP 呢?是不是每次都會(huì)分配空間呢?不是的。Zend Engine 有著復(fù)雜的系統(tǒng)來面對(duì)這個(gè)問題,當(dāng) PHP 的任意對(duì)象沒有改變的時(shí)候,內(nèi)存中只有一個(gè) copy,只有當(dāng)改變的時(shí)候才會(huì)分配空間,而這一切對(duì)于用戶都是透明的。

另外,作業(yè)要求從 URL 中提取主機(jī)名。這在 PHP 中是很簡(jiǎn)單的,當(dāng)然也是 Perl 的強(qiáng)項(xiàng),而 Ruby 只是把它很優(yōu)雅地 OO 化了而已。

先前說過,斜線字符串代表規(guī)則表達(dá)式,而 RegExp 有一個(gè)方法叫做 match,其實(shí)和 PHP 的 preg_match 是一樣的。Ruby 也有 =~ 操作符,但是由于本人對(duì) Perl 實(shí)在不熟,所以沒覺得好用,就用 match 吧。match 方法把 data[6] 和 RegExp 對(duì)比,然后返回捕獲的結(jié)果。由于 Ruby 的對(duì)象機(jī)制很靈活,所以,直接在這個(gè) function call 的后面加上 [1] 也可以([0] 是整個(gè) match 的字符串)。

這次作業(yè)的 log 文件很簡(jiǎn)單,沒有 https/ftp 什么的,所以上面沒有寫出,當(dāng)然要寫也是很容易的。

好了,到了這一步,用 puts 來測(cè)試一下結(jié)果,成功,然后因?yàn)槭亲x入文件,每一行一個(gè)記錄,所以需要有一個(gè)東西來儲(chǔ)存記錄。首先想到的,是:

- 讀入所有 log 并儲(chǔ)存為數(shù)據(jù)結(jié)構(gòu)
- 遍歷數(shù)據(jù)結(jié)構(gòu)提取需要的數(shù)據(jù)并匯報(bào)

因?yàn)橛腥N匯報(bào)模式,都需要訪問整個(gè) log 數(shù)據(jù),所以,最好用類。

首先建立一個(gè) Log 類:

  1. class Log
  2. end
復(fù)制代碼


大寫開頭的名字在 Ruby 中代表常量,也用于命名類(關(guān)于這個(gè)問題 Pragmatic Ruby 里面有詳細(xì)解釋為什么)。

Ruby 不需要 {} 也不需要冒號(hào),結(jié)尾用 end 表示。(其實(shí)這一點(diǎn)我不太喜歡,因?yàn)椴皇呛芤恢拢琸eyword 直接用 end 結(jié)尾,但另外一些卻必須用 do 開始 end 結(jié)尾。)

現(xiàn)在,在 Log 實(shí)例化的時(shí)候,我希望它完成讀取 log 文件并儲(chǔ)存在自己的一個(gè)成員變量:

  1. class Log
  2.     def initialize(filename, cpu_time)
  3.         @log = Array.new
  4.         @cpu_time = cpu_time

  5.         File.open(filename, 'r') do |file|
  6.             while (line = file.gets)
  7.                 data = line.split(/\s/)

  8.                 columns = Hash.new
  9.                 columns[:user_ip]     = data[2]
  10.                 columns[:http_status] = data[3]
  11.                 columns[:file_size]   = data[4]
  12.                 columns[:http_query]  = data[5]
  13.                 columns[:host]        = /^http:\/\/([^\/]+)/.match(data[6])[1]
  14.                 columns[:mime]        = data[7]

  15.                 @log << columns
  16.             end
  17.         end
  18.     end
  19. end
復(fù)制代碼


注:因?yàn)檫@次作業(yè)交晚了,所以比較趕工,程序也比較亂,這不是 Ruby 的作風(fēng),也不是我的作風(fēng)……

def 是 Ruby 中定義方法的 keyword。initialize 是類實(shí)例化的時(shí)候會(huì)使用的函數(shù),大概類似于 constructor。這個(gè)函數(shù)使用兩個(gè)參數(shù),一個(gè)是文件名,一個(gè)是,是否記錄 CPU 時(shí)間。

@log 和 @cpu_time 是這個(gè)類的實(shí)例變量(instance variable),前面加一個(gè) @ 以辯別。加 @@ 則是類變量(class variable)。

首先賦值,@log 用來記錄日志條目,所以用 Array.new,這也是我現(xiàn)在知道的為數(shù)不多的 Ruby 數(shù)據(jù)結(jié)構(gòu)之一……
@cpu_time 直接復(fù)制參數(shù)即可。

下面,Ruby magic 來了。下面的部分基本上屬于 Ruby specific 的了。

File 是 Ruby 的文件類,用于簡(jiǎn)單文件操作。要操作文件,必須先打開文件。查了 Ruby Ref 之后,發(fā)現(xiàn) File 有 new 和 open 兩個(gè)方法用于打開文件,有何不同?

參數(shù)都是一樣的,不同在于,new 直接打開文件,完了。
open 可以像 new 一樣用,也可以:附加一個(gè) block。

如果 open 附加一個(gè) block,那么在 block 執(zhí)行完畢之后,文件將會(huì)自動(dòng)關(guān)閉,非常方便。

剛剛開始看到這里的時(shí)候,也許比較混亂吧,我剛接觸這個(gè)東西的時(shí)候也一樣。下面就詳細(xì)解釋一下工作原理吧。

用 pseudo-code 寫一個(gè)文件類,只有三個(gè)方法:

  1. class File
  2. {
  3.     method open(filename, mode)
  4.     {
  5.         // open file...
  6.         return handle
  7.     }

  8.     method close()
  9.     {
  10.         // close file handle...
  11.     }
  12. }
復(fù)制代碼


好,現(xiàn)在我們要用這個(gè)類了,但是每次 open,我們都必須記得要對(duì)應(yīng) close。有沒有辦法讓這個(gè)過程自動(dòng)化?有的。加入下面一個(gè)方法:

  1. class File
  2. {
  3.     // method open()...
  4.     // method close()...

  5.     method open_and_proc(filename, mode, &block)
  6.     {
  7.         file_handle = open_file(filename)   # blah blah
  8.         yield file_handle
  9.         self.close
  10.     }
  11. }
復(fù)制代碼


這里,yield 和 return 一樣,都將離開正在執(zhí)行的代碼塊(method open_and_proc)。但是不一樣的是,yield 是暫時(shí)離開去執(zhí)行第三個(gè)參數(shù) block(也是一個(gè)代碼塊),然后再回來繼續(xù)執(zhí)行 open_and_proc。不太好理解?再換一個(gè)例子吧。

在 Ruby 中,有一個(gè)很 handy 的整數(shù)方法 times。比如說,想輸出 10 句 "Hello World!",那么可以這樣:

  1. 10.times do
  2.     puts "Hello World!"
  3. end
復(fù)制代碼


這段程序?qū)⑤敵?10 次 Hello, World!

這樣也許不太清楚,下面這個(gè)版本用 {} 代替 do...end,應(yīng)該更清楚一些。

  1. 10.times {
  2.     puts "Hello World!"
  3. }
復(fù)制代碼


看看 Integer 中 times 的定義吧。如果用 Ruby 表示,times 的定義大概是這樣:

  1. class Integer
  2.     def times(&block)
  3.         i = 0

  4.         while (i < self - 1)
  5.             yield i
  6.         end
  7.     end
  8. end
復(fù)制代碼


block 是代碼塊,而 yield 執(zhí)行這個(gè)代碼塊,而且傳入一個(gè)參數(shù)。代碼塊如何得到這個(gè)參數(shù)?用 ||。這個(gè)記號(hào) Smalltalker 一定很熟悉。所以這個(gè)代碼塊還可以這樣用:

10.times { |i|
    puts "#{i} Hello World!"
}
[/code]


注意上面的 |i|,這樣就接受了這個(gè)參數(shù)。上面的代碼將輸出:

0 Hello World!
1 Hello World!
2 Hello World!
3 Hello World!
4 Hello World!
5 Hello World!
6 Hello World!
7 Hello World!
8 Hello World!
9 Hello World!

也許一開始這種作法并不討好,感覺還有些混亂。當(dāng)你實(shí)際用到之后,就會(huì)慢慢覺得很方便了。

在 Ruby 中,iterator 的實(shí)現(xiàn)也是用這個(gè)辦法。不管是 Hash 還是 Array,都有 each 方法,這個(gè)方法即是用來遍歷數(shù)據(jù)結(jié)構(gòu)。例如:

  1. arr = ["foo", "bar", "egg", "span"]
復(fù)制代碼


這是一個(gè)數(shù)組,如果在 PHP/C... 中,要輸出這個(gè)數(shù)組所有項(xiàng)目,可以用:

  1. foreach (arr as a) {
  2.     print a
  3. }

  4. // 或者...

  5. for (i = 0, max = count(arr); i < max; ++i)
  6.     print a
復(fù)制代碼


第二種方法需要你了解這個(gè)數(shù)據(jù)結(jié)構(gòu)的內(nèi)部結(jié)構(gòu),不可取。第一種方法在 Ruby 中則是用 block 實(shí)現(xiàn)的:

  1. # Ruby code

  2. arr.each {
  3.     |item|
  4.     puts item
  5. }
復(fù)制代碼


現(xiàn)在回到 File 類。File 類提供了一個(gè) open 方法,這個(gè)方法可以接受一個(gè) block。如果傳入 block,則 File 將打開文件,傳入 block,執(zhí)行 block,執(zhí)行完后關(guān)閉文件,非常好用。

  1. while (line = file.gets)
  2. end
復(fù)制代碼


這一段則是 Ruby 的簡(jiǎn)單文件讀取,gets 將讀取文件中的一行,由于有自帶計(jì)數(shù)器,只需要上面一句即可遍歷整個(gè)文件。

后面只需要抄最先的代碼,即可把每行的數(shù)據(jù)進(jìn)行分析并輸入一個(gè) Hash。把這個(gè) Hash 加入 @log 數(shù)組,用 << 操作符,我最先猜 append 方法,結(jié)果沒有,看來偶爾也會(huì)失誤(我猜 split,match,規(guī)則表達(dá)式語法都猜對(duì)了,open 也基本上猜對(duì)了)。

然后需要根據(jù)要求加入幾個(gè)方法 parse/summary/bill,分別對(duì)應(yīng)幾個(gè)需求。然后再加入主驅(qū)動(dòng)程序,這個(gè)程序就算完了。全程序:

  1. #!/usr/local/bin/ruby

  2. # class definition

  3. class Log
  4.     def initialize(filename, cpu_time)
  5.         @log = Array.new
  6.         @cpu_time = cpu_time

  7.         File.open(filename, 'r') do |file|
  8.             while (line = file.gets)
  9.                 data = line.split(/\s/)

  10.                 columns = Hash.new
  11.                 columns[:user_ip]     = data[2]
  12.                 columns[:http_status] = data[3]
  13.                 columns[:file_size]   = data[4]
  14.                 columns[:http_query]  = data[5]
  15.                 columns[:host]        = /^http:\/\/([^\/]+)/.match(data[6])[1]
  16.                 columns[:mime]        = data[7]

  17.                 @log << columns
  18.             end
  19.         end
  20.     end
  21.    
  22.     def parse(user_ip)
  23.         all_line   = 0
  24.         total_line = 0
  25.         total_get  = 0
  26.         total_200  = 0
  27.         total_byte = 0
  28.         host_count = Hash.new
  29.         mime_count = Hash.new
  30.         
  31.         @log.each do |a_log|
  32.             all_line += 1
  33.         
  34.             next if (a_log[:user_ip] != user_ip)
  35.             
  36.             total_line += 1
  37.             total_get  += 1 if (a_log[:http_query] == 'GET')
  38.             total_200  += 1 if (a_log[:http_status] == '200')
  39.             total_byte += a_log[:file_size].to_i

  40.             if (host_count[a_log[:host]])
  41.                 host_count[a_log[:host]] += 1
  42.             else
  43.                 host_count[a_log[:host]] = 1
  44.             end

  45.             if (mime_count[a_log[:mime]])
  46.                 mime_count[a_log[:mime]] += 1
  47.             else
  48.                 mime_count[a_log[:mime]] = 1
  49.             end
  50.         end

  51.         avg_byte   = total_byte / total_200
  52.         
  53.         max = 0
  54.         max_key = ''
  55.         host_count.each do |key, val|
  56.             if (val > max)
  57.                 max = val
  58.                 max_key = key
  59.             end
  60.         end

  61.         fav_host   = max_key

  62.         max = 0
  63.         max_key = ''
  64.         mime_count.each do |key, val|
  65.             if val > max
  66.                 max = val
  67.                 max_key = key
  68.             end
  69.         end
  70.         
  71.         fav_mime = max_key
  72.         
  73.         puts "Total number of line: #{total_line}"
  74.         puts "Total GET: #{total_get}"
  75.         puts "Total HTTP 200: #{total_200}"
  76.         puts "Total bytes: #{total_byte}"
  77.         puts "Most visited host: #{fav_host}"
  78.         puts "Most visited mime: #{fav_mime}"
  79.         
  80.         if (@cpu_time)
  81.             t = Process.times
  82.             puts "CPU Time per line: #{t.utime / all_line}"
  83.         end
  84.     end
  85.    
  86.     def summary
  87.         total_line  = 0
  88.         total_byte  = 0
  89.         user_byte   = Hash.new
  90.         total_get   = 0
  91.         top_10_host = Hash.new
  92.         top_10_mime = Hash.new
  93.         
  94.         @log.each do |a_log|
  95.             total_line += 1
  96.             total_byte += a_log[:file_size].to_i
  97.             
  98.             if (user_byte[a_log[:user_ip]])
  99.                 user_byte[a_log[:user_ip]] += a_log[:file_size].to_i
  100.             else
  101.                 user_byte[a_log[:user_ip]] = a_log[:file_size].to_i
  102.             end
  103.             
  104.             total_get += 1 if a_log[:http_status] == '200' # only succeed GETs
  105.             
  106.             if (top_10_host[a_log[:host]])
  107.                 top_10_host[a_log[:host]] += 1
  108.             else
  109.                 top_10_host[a_log[:host]] = 1
  110.             end
  111.             
  112.             if (top_10_mime[a_log[:mime]])
  113.                 top_10_mime[a_log[:mime]] += 1
  114.             else
  115.                 top_10_mime[a_log[:mime]] = 1
  116.             end
  117.         end
  118.         
  119.         top_10_host = top_10_host.sort do |a, b|
  120.             b[1] <=> a[1]
  121.         end
  122.         
  123.         top_10_mime = top_10_mime.sort do |a, b|
  124.             b[1] <=> a[1]
  125.         end
  126.         
  127.         puts "Total line read: #{total_line}"
  128.         puts "Total bytes consumed: #{total_byte}"
  129.         puts "Byte consumed grouped by user:"

  130.         user_byte.each do |user, byte|
  131.             puts "#{user} used #{byte} bytes."
  132.         end

  133.         puts "Total successful GET: #{total_get}"
  134.         
  135.         puts "Top 10 hosts:"
  136.         i = 1
  137.         top_10_host.to_a.each do |a|
  138.             puts "No.#{i}  #{a[0]}  hits: #{a[1]}"

  139.             i += 1            
  140.             break if i > 10
  141.         end
  142.         
  143.         puts "Top 10 mime:"
  144.         i = 1
  145.         top_10_mime.to_a.each do |a|
  146.             puts "No.#{i}  #{a[0]}  hits: #{a[1]}"

  147.             i += 1            
  148.             break if i > 10
  149.         end

  150.         if (@cpu_time)
  151.             t = Process.times
  152.             puts "CPU Time per line: #{t.utime / total_line}"
  153.         end
  154.     end
  155.    
  156.     def bill
  157.         total_line = 0
  158.         user_bill  = Hash.new
  159.         world_url  = 0
  160.         world_byte = 0
  161.         domes_url  = 0
  162.         domes_byte = 0
  163.         
  164.         @log.each do |a_log|
  165.             total_line += 1

  166.             if (user_bill[a_log[:user_ip]] == nil)
  167.                 user_bill[a_log[:user_ip]] = Hash.new
  168.                 user_bill[a_log[:user_ip]][:world_url]  = 0
  169.                 user_bill[a_log[:user_ip]][:world_byte] = 0
  170.                 user_bill[a_log[:user_ip]][:domes_url]  = 0
  171.                 user_bill[a_log[:user_ip]][:domes_byte] = 0
  172.             end

  173.             if (a_log[:host].match(/\.au$/))
  174.                 user_bill[a_log[:user_ip]][:domes_url] += 1
  175.                 user_bill[a_log[:user_ip]][:domes_byte] += a_log[:file_size].to_i
  176.             else
  177.                 user_bill[a_log[:user_ip]][:world_url] += 1
  178.                 user_bill[a_log[:user_ip]][:world_byte] += a_log[:file_size].to_i
  179.             end
  180.         end

  181.         user_bill.each do |user, bill|
  182.             total = 0
  183.             puts "User: #{user}"
  184.             puts "International visits: #{bill[:world_byte]} bytes in #{bill[:world_url]} urls"
  185.             total += bill[:world_byte].to_f / 1024
  186.             puts "Domestic visits: #{bill[:domes_byte]} bytes in #{bill[:domes_url]} urls"
  187.             total += bill[:domes_byte].to_f / 1024 * 4
  188.             puts "Total cost: $#{total}"
  189.             puts '============================================'
  190.         end
  191.         
  192.         if (@cpu_time)
  193.             t = Process.times
  194.             puts "CPU Time per line: #{t.utime / total_line}"
  195.         end
  196.     end
  197. end

  198. # end class definition

  199. if (ARGV.size < 1)
  200.     puts 'Usage: logwhacker [-u ip_address] [-s] [-t] log_file'
  201.     exit
  202. end

  203. if (ARGV[0] == '-u')
  204.     if (ARGV[1].match(/[1-9][0-9]{0,2}\.[1-9][0-9]{0,2}\.[1-9][0-9]{0,2}\.[1-9][0-9]{0,2}/))
  205.         mode = 'USER'
  206.         user_ip = ARGV[1]
  207.         ARGV.shift   # removes '-u'
  208.         ARGV.shift   # removes ip
  209.     else
  210.         puts 'Error: wrong arguments for -u.'
  211.         exit
  212.     end
  213. elsif (ARGV[0] == '-s')
  214.     mode = 'SUM'
  215.     ARGV.shift  # removes '-s'
  216. elsif (ARGV[0] == '-b')
  217.     mode = 'BILL'
  218.     ARGV.shift  # removes '-b'
  219. else
  220.     puts 'Error: arguments required [-u] or [-s] or [-b]'
  221.     exit
  222. end

  223. if (ARGV[0] == '-t')
  224.     ARGV.shift  # removes '-t'
  225.     cpu_time = true
  226. end

  227. if (ARGV[0] == nil)
  228.     puts 'Error: log file name required.'
  229.     exit
  230. else
  231.     filename = ARGV.shift
  232. end

  233. logwhacker = Log.new(filename, cpu_time)

  234. if (mode == 'USER')
  235.     logwhacker.parse(user_ip)
  236. elsif (mode == 'SUM')
  237.     logwhacker.summary
  238. elsif (mode == 'BILL')
  239.     logwhacker.bill
  240. end
復(fù)制代碼


[ 本帖最后由 dz902 于 2007-8-23 02:40 編輯 ]

論壇徽章:
0
2 [報(bào)告]
發(fā)表于 2007-08-14 20:44 |只看該作者
從 PHPer 的角度,需要解釋的地方有:

  • Ruby 像 Python 一樣,支持 statement modifier,例如:


    1. if (foo == true)
    2.     next
    3. end

    4. // 可以寫為

    5. next if (foo == true)
    復(fù)制代碼


    這個(gè)屬于蘿卜青菜的問題,雖然個(gè)人覺得這種方法有些混亂,但是有的時(shí)候也使代碼很清晰。
  • Ruby 是 strong typed,所以,你會(huì)看到 to_i/to_a 這一類的類型轉(zhuǎn)換。
  • Hash.each 將會(huì)傳入 key 和 value 兩個(gè)值,接收方法是 |key, value|。
  • 字符以及變量混合輸出(string interpolation)的時(shí)候,必須用 "#{var}"的辦法,但這個(gè)方法比 PHP 要強(qiáng)大,好像可以執(zhí)行任意語句。
  • 取得 CPU 時(shí)間用 Process.times (如果系統(tǒng)支持),這個(gè)函數(shù)的資料可以查 Ruby Doc。
  • 取得命令行的參數(shù),用 ARGV,這是一個(gè) Array,用 Array 的 shift 方法可以提取參數(shù)。
  • 程序第一行用 shebang 指出 Ruby 的位置即可直接執(zhí)行(廢話)。


結(jié)語

這次的作業(yè)因?yàn)橼s工有很多重復(fù)的部分,結(jié)構(gòu)很差。但是 Ruby 的語法優(yōu)勢(shì)體現(xiàn)了出來,從寫第一行到最后之用了兩個(gè)小時(shí)左右,包括下載日志文件和查 Ruby Doc 的時(shí)間。不得不說,在沒有實(shí)際使用之前,我覺得 Ruby 的文檔很不好用,可能是受了 PHP 的影響吧。但是實(shí)際使用中,Ruby 的文檔還是比較好用的,很清楚,方法的名字很多很好猜,一猜多半也能猜對(duì),但偶爾也會(huì)有簡(jiǎn)寫很奇怪的(比如和 clone 一樣的 duplicate 縮寫是 dup)。正則表達(dá)式也很簡(jiǎn)單,和 Javascript 的正則很像。PHP,或者說所有動(dòng)態(tài)腳本語言的優(yōu)勢(shì),Ruby 都繼承了。

另外,說 Ruby 「一切都是對(duì)象」這個(gè)觀念很難接收的話,這個(gè)我很無語。因?yàn),Ruby 的 OO 非常地清晰,比 C++ 要簡(jiǎn)單,又不像 Java 要求 main 都要 OO。你自己的代碼完全可以不用 OO,就用 def 函數(shù)就可以了。所有的函數(shù)都按照類型(或者 class)來分類,這樣很有條理,也避免了命名沖突。

block 也是 Ruby 很強(qiáng)大的功能之一,雖然不是 Ruby 原創(chuàng),但是 Ruby 使得這個(gè)功能很簡(jiǎn)單。Closure 在 Ruby 中也能很簡(jiǎn)單實(shí)現(xiàn),不但寫代碼簡(jiǎn)單,看起來也很容易理解。Ruby 的 block 和 yield 和 Python 也有相似(本來就是同宗嘛),但是不同的是 Python 用 yield 實(shí)現(xiàn) generator 和 lazy evaluation,非常簡(jiǎn)單(贊,Python 的好功能);而 Ruby 的 yield 雖然更復(fù)雜,卻也更強(qiáng)大(單就 yield 來說)。

Ruby 不只是 RoR,一個(gè)語言不可能僅僅因?yàn)橐粋(gè)框架而收到重視,其本身也一定有很可取的地方,F(xiàn)在 Ruby 的社區(qū)也越來越大,下一代的 runtime/virtual compiler 也在開發(fā)中。衷心希望 Ruby 越走越好。

以后如果還有類似的體驗(yàn),我還會(huì)發(fā)上來。希望 Ruby 版人氣能夠高起來。I like it Ruby way~

PS

  • 我也喜歡 PHP~歡迎交流~
  • 最近也在看 Python~歡迎交流~




附一個(gè) 10000 行的測(cè)試 log 文件

sample.rar (145.3 KB, 下載次數(shù): 412)

[ 本帖最后由 dz902 于 2007-8-18 02:23 編輯 ]

55632873_4c0eba44ec.jpg (99.96 KB, 下載次數(shù): 228)

55632873_4c0eba44ec.jpg

論壇徽章:
0
3 [報(bào)告]
發(fā)表于 2007-08-15 09:02 |只看該作者
支持一下!
我也想學(xué)的,不過最近沒什么動(dòng)力了。

論壇徽章:
1
2015年辭舊歲徽章
日期:2015-03-03 16:54:15
4 [報(bào)告]
發(fā)表于 2007-08-15 13:30 |只看該作者
支持!

論壇徽章:
0
5 [報(bào)告]
發(fā)表于 2007-08-15 15:49 |只看該作者
感到了ruby的強(qiáng)大

論壇徽章:
0
6 [報(bào)告]
發(fā)表于 2007-08-21 15:45 |只看該作者
頂一個(gè)

論壇徽章:
0
7 [報(bào)告]
發(fā)表于 2007-08-22 20:45 |只看該作者
不僅程序而且連帖子的排版格式看上去都是賞心悅目,樓主我支持你!

論壇徽章:
0
8 [報(bào)告]
發(fā)表于 2007-08-22 22:17 |只看該作者
原帖由 DennisRitchie 于 2007-8-22 22:45 發(fā)表
不僅程序而且連帖子的排版格式看上去都是賞心悅目,樓主我支持你!


呵謝謝,我是完美主義者,所有的帖子都是這樣,標(biāo)點(diǎn)也不會(huì)漏掉。這是對(duì)看客的尊重,也是對(duì)語言的尊重~

論壇徽章:
0
9 [報(bào)告]
發(fā)表于 2007-08-22 22:32 |只看該作者
原帖由 dz902 于 2007-8-22 22:17 發(fā)表


呵謝謝,我是完美主義者,所有的帖子都是這樣,標(biāo)點(diǎn)也不會(huì)漏掉。這是對(duì)看客的尊重,也是對(duì)語言的尊重~

這種認(rèn)真的態(tài)度正是國(guó)內(nèi)大部分人所缺少的,如果樓主寫一本教材的話,我一定會(huì)買的!

論壇徽章:
0
10 [報(bào)告]
發(fā)表于 2007-08-22 22:46 |只看該作者
原帖由 DennisRitchie 于 2007-8-23 00:32 發(fā)表

這種認(rèn)真的態(tài)度正是國(guó)內(nèi)大部分人所缺少的,如果樓主寫一本教材的話,我一定會(huì)買的!


再次謝謝,希望有那么一天吧~
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP