《Head First C 中文版》審讀筆記(四)
八卦新聞:用 RSS 讀新聞
《Head First C 中文版》第 9 章 程式與系統呼叫
第 416 頁八卦新聞:用 RSS 讀新聞
:
RSS 源是網站釋出新聞的常用方式。RSS 源其實就是一個 XML 檔案,裡面有新聞的摘要和連結。當然,你完全有能力寫一個直接從網頁讀取 RSS 檔案的 C 程式,但這涉及一些你沒有接觸過的程式設計概念。為什麼不找一個程式幫忙處理 RSS 檔案呢?
RSS Gossip 是一個 Python 小指令碼,它可以根據某個關鍵字在 RSS 源中查詢新聞。你必須先安裝 Python 才能執行這個指令碼,一旦有了 Python 和 rssgossip.py,就可以像這樣搜尋新聞:
下面就是在我的 Arch Linux 64-bit 作業系統中的執行結果:
> python2 --version
Python 2.7.4
> python --version
Python 3.3.1
> export RSS_FEED=http://rss.cnn.com/rss/edition.rss
> python2 rssgossip.py 'China'
Why China involved in Africa's health
China reduces banking lifeline to N. Korea
China's youngest comrades
> python2 rssgossip.py -u 'China'
Why China involved in Africa's health
http://edition.cnn.com/2013/05/09/opinion/china-africa-health/index.html?eref=edition
China reduces banking lifeline to N. Korea
http://edition.cnn.com/2013/05/08/business/china-north-korea-banking/index.html?eref=edition
China's youngest comrades
http://edition.cnn.com/2013/04/30/world/asia/china-young-communists/index.html?eref=edition
請注意以下幾點:
- 大多數 Linux 作業系統中已經預裝了 Python 了,一般來說不需要另外再安裝了。但是要注意,本書中的 rssgossip.py 指令碼需要 Python2,不能使用 Python3。而現在很多 Linux 中已經是 Python3 了。如果是這樣,就需要使用 python2 代替 python 。
- RSS 連結是通過 RSS_FEED 環境變數傳遞給 rssgossip.py 指令碼的。
- 書中的 RSS 連結是作者編造的,實際上並不存在,需要替換為我們自己找到的 RSS 連結。
- rssgossip.py 可帶 -u 引數執行,將會顯示新聞的 URL。這一點很重要,在後面我們將會用到。
下面就是 rssgossip.py 源程式。也可以到文末的參考資料[2]中下載。
import urllib
import os
import re
import sys
import string
import unicodedata
import getopt
from xml.dom import minidom
def usage():
print("Usage:\npython rssgossip.py [-uh] <search-regexp>")
try:
opts, args = getopt.getopt(sys.argv[1:], "uh", ["urls", "help"])
except getopt.GetoptError, err:
print str(err)
usage()
sys.exit(2)
include_urls = False
for o, a in opts:
if o == "-u":
include_urls = True
elif o in ("-h", "--help"):
usage()
sys.exit()
else:
assert False, "unhandled option"
searcher = re.compile(args[0], re.IGNORECASE)
for url in string.split(os.environ['RSS_FEED']):
feed = urllib.urlopen(url)
try:
dom = minidom.parse(feed)
forecasts = []
for node in dom.getElementsByTagName('title'):
txt = node.firstChild.wholeText
if searcher.search(txt):
txt = unicodedata.normalize('NFKD', txt).encode('ascii', 'ignore')
print(txt)
if include_urls:
p = node.parentNode
link = p.getElementsByTagName('link')[0].firstChild.wholeText
print("\t%s" % link)
except:
sys.exit(1)
案例研究:在瀏覽器中開啟新聞
本書第 10 章 程式間通訊
第 444 頁案例研究:在瀏覽器中開啟新聞
:
假設你想在瀏覽器中開啟 rssgossip.py 指令碼找到的新聞連結,你將在父程式中執行程式,在子程式中執行 rssgossip.py 。需要建立管道,把 rssgossip.py 的輸出和程式的輸入連線起來。
程式碼熟食:在瀏覽器中開啟網頁
第 446 頁程式碼熟食:在瀏覽器中開啟網頁
:
程式需要用機器上的瀏覽器開啟網頁,但不同作業系統與程式互動的方式不同,因此實現起來多少有些難度。
好在兼職演員已經為你寫好了這部分程式碼,它能夠在絕大多數系統上開啟網頁。但他們好像還有要事在身,所以只採用了最簡單的實現方式:
void open_url(const char *url) { char launch[255]; sprintf(launch, "cmd /c start %s", url); // 在Windows上開啟網頁。 system(launch); sprintf(launch, "x-www-browser '%s' &", url); // 在Linux上開啟網頁。 system(launch); sprintf(launch, "open '%s'", url); // 在Mac上開啟網頁。 system(launch); }
程式碼用了三條命令,它們分別可以在 Mac、Windows 和 Linux 上開啟 URL 。每次都有兩條命令會失敗,但只要有一條成功就行了。
實際上,在我的 Linux 機器上,三條命令都失敗了。這是怎麼回事?原來, x-www-browser 一般用於 Ubuntu 等 Linux 發行版中。雖然 Ubuntu 很多人使用,而且我以前也用過,但現在我用的是 Arch Linux,它不支援 x-www-browser 。解決方案是使用 xdg-open 代替,它可用於大多數 Linux 作業系統。而且 xdg-open 不僅可以開啟網頁,而且可以開啟作業系統支援的任意檔案,這和上述程式中其它兩個作業系統的做法一致。這樣,open_url 函式也可以改名為 open_thing 函式。詳細資訊請參見文末的參考資料[3]。
連線管道
第 448 頁連線管道:練習解答
:
大部分程式碼已經寫好了,你只需要填寫用管道連線父子程式的那部分。為了節約空間,我們去掉了 #include 語句、error() 和 open_url() 函式。
int main(int argc, char *argv[])
{
char *phrase = argv[1]; // 下一行的 RSS 可以根據實際需要替換。
char *vars[] = {"RSS_FEED=http://rss.cnn.com/rss/edition.rss", NULL};
int fd[2]; // 這個陣列將儲存管道描述符
if (pipe(fd) == -1) { // 建立管道,並把描述符儲存在fd[0]和fd[1]中。
error("Can't create pipe"); // 為了防止管道建立失敗,需要檢查pipe()的返回值。
}
pid_t pid = fork();
if (pid == -1) {
error("Can't fork process");
}
if (!pid) { // 這裡你在子程式中。
dup2(fd[1], 1); // 把標準輸出設為管道的寫入端
close(fd[0]); // 子程式不會讀取管道,所以我們將關閉讀取端
if (execle("/usr/bin/python2", "/usr/bin/python2", "rssgossip.py",
"-u", phrase, NULL, vars) == -1) { // -u 讓指令碼顯示新聞URL。
error("Can't run script");
}
}
dup2(fd[0], 0); // 從這裡開始你就在父程式中。
close(fd[1]); // 關閉管道的寫入端,因為父程式不需要向管道寫資料。
char line[255];
while (fgets(line, 255, stdin)) { // 將從標準輸入讀取資料,因為管道連到了標準輸入。
if (line[0] == '\t') // 如果line以Tab開頭... //上一行的stdin也可以用fd[0]。
open_url(line + 1); // ...就說明它是URL。
}
return 0;
}
注意, 在上述程式碼中:
- 我們使用自己的 RSS 源替換了書中虛構的 RSS 源。
- 使用 /usr/bin/python2 替換了 /usr/bin/python 。
夜未眠:還在為錯誤程式碼煩惱?
本書第 434 頁夜未眠:還在為錯誤程式碼煩惱?
:
每次你在系統呼叫時都需要反反覆覆寫那些錯誤處理程式碼。還猶豫什麼!趕快使用我們的獨家祕方,我們將向你展示如何重用錯誤程式碼,從此你將告別重複程式碼:
……
首先,需要把處理程式碼放到一個單獨的 error() 函式中,然後把 return 語句換成 exit() 系統呼叫。
void error(const char *msg) { fprintf(stderr, "%s: %s\n", msg, strerror(errno)); exit(1); // exit(1)會立刻終止程式,並把退出狀態置1。 }
現在就可以把那些煩人的錯誤檢查程式碼換成:
……
警告:每次程式執行只有一次呼叫 exit() 的機會,“程式突然結束恐懼症”患者慎用。
補上 include 語句
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
編譯程式碼並執行程式
本書第 449 頁(使用我機器上的執行結果代替了書中的):
編譯程式碼並執行程式,出現了:
> gcc news_opener.c -o news_opener > ./news_opener 'China' sh: cmd: 未找到命令 sh:行1: open: 未找到命令 sh: cmd: 未找到命令 sh:行1: open: 未找到命令 sh: cmd: 未找到命令 sh:行1: open: 未找到命令 已在現有的瀏覽器會話中建立新的視窗。 已在現有的瀏覽器會話中建立新的視窗。 已在現有的瀏覽器會話中建立新的視窗。 >
太棒了,程式工作了。
news_opener 程式在一個獨立的程式中執行了 rssgossip.py,並讓它顯示找到新聞的 URL 。所有本來應該傳送到螢幕上的輸出現在通過管道重定向到 news_opener 父程式,news_opener 就可以在瀏覽器中開啟新聞了。
管道是連線程式的好辦法。現在你不但能夠執行程式,控制它們的環境,而且還能獲取程式的輸出,這樣就可以實現很多功能。任何一個能夠在命令列中執行的程式你都可以在 C 程式碼中呼叫並控制它。
參考資料
相關文章
- 《Head First Java》20201017讀書筆記Java筆記
- 《Head First Java》20200927讀書筆記Java筆記
- 《Head First Java》20201009讀書筆記Java筆記
- Head First設計模式讀書筆記設計模式筆記
- Head first PHP & MySQL 中文版pdfPHPMySql
- Head First HTML 與 CSS(第二版)學習筆記HTMLCSS筆記
- Head First Java學習筆記(7):繼承與多型Java筆記繼承多型
- Head First 設計模式筆記 3.裝飾者模式設計模式筆記
- Head First C 電子書pdf下載
- Head First Python (一)Python
- Head First HTML and CSS (八)HTMLCSS
- 《Head First Android》讀後感,電子書PDF下載Android
- elasticsearch-head 筆記Elasticsearch筆記
- 《Head First 設計模式》:策略模式設計模式
- 「HEAD-FIRST」之觀察者模式模式
- 《Head First 設計模式》:模板方法模式設計模式
- 《Head First 設計模式》:單件模式設計模式
- 《Head First 設計模式》:外觀模式設計模式
- 《Head First 設計模式》:狀態模式設計模式
- 《Head First 設計模式》:剩下的模式設計模式
- 《Head First 設計模式》:組合模式設計模式
- Head First 設計模式(1)-----策略模式設計模式
- c++學習筆記(四)C++筆記
- 《Head First 設計模式》:工廠方法模式設計模式
- 《Head First 設計模式》:觀察者模式設計模式
- 《Effective C++》讀書筆記C++筆記
- C++讀書筆記:字串C++筆記字串
- 《C#本質論》讀書筆記<四>值型別再解析C#筆記型別
- Head First 設計模式(3)----裝飾者模式設計模式
- Head First 設計模式 —— 13. 代理 (Proxy) 模式設計模式
- 《C缺陷與陷阱》讀書筆記筆記
- Head First 設計模式(2)---觀察者(Observer)模式設計模式Server
- head first java第一章的學習Java
- 《Head First 設計模式》:與設計模式相處設計模式
- Head First 設計模式 —— 14. 複合 (Compound) 模式設計模式
- C++筆記 14:審慎使用異常規格(exception specifications)C++筆記Exception
- [head first 設計模式] 第一章 策略模式設計模式
- Head First 設計模式 —— 09. 模版方法 (Template Method) 模式設計模式
- Vue2.0原始碼閱讀筆記(四):nextTickVue原始碼筆記