package main import ( "fmt" "golang.org/x/net/html" "net/http" "os" ) func main() { for _, url := range os.Args[1:] { fmt.Println(os.Args[1:]) links, err := findLinks(url) if err != nil { fmt.Fprintf(os.Stderr, "findlinks2: %vn", err) continue } for _, link := range links { fmt.Println(link) } } } // findLinks發起一個HTTP的GET請求,解析返回的HTML頁面,並返回所有連結 func findLinks(url string) ([]string, error) { resp, err := http.Get(url) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { resp.Body.Close() return nil, fmt.Errorf("getting %s: %s", url, resp.Status) } doc, err := html.Parse(resp.Body) resp.Body.Close() if err != nil { return nil, fmt.Errorf("parsing %s as HTML: %v", url, err) } return visit(nil, doc), nil } // 將節點 n 中的每個連結新增到結果中 func visit(links []string, n *html.Node) []string { if n == nil { return links } if n.Type == html.ElementNode && n.Data == "a" { for _, a := range n.Attr { if a.Key == "href" { links = append(links, a.Val) } } } // 可怕的遞迴,非常不好理解。 return visit(visit(links, n.FirstChild), n.NextSibling) }findLinks 函數有 4 個返回語句,每一個語句返回一對值,前 3 個返回語句將函數從 http 和 html 包中獲得的錯誤資訊傳遞給呼叫者,第一個返回語句中,錯誤直接返回,第二個返回語句和第三個返回語句則使用 fmt.Errorf 格式化處理過的附加上下文資訊,如果 findLinks 呼叫成功,最後一個返回語句將返回連結的 slice,且 error 為空。
links, err := findLinks(url)
忽略其中一個返回值可以將它賦給一個空識別符號_
。links, _ := findLinks(url) // 忽略的錯誤
一個含有多個值的函數返回值可以是呼叫另一個含有多個返回值的函數得到的,就像下面的函數,這個函數的行為和 findLinks 類似,只是多了一個記錄引數的動作。
func findLinksLog(url string) ([]string, error) {
log.Printf("findLinks %s", url)
return findLinks(url)
}
log.Println(findLinks(url))
links, err := findLinks(url)
log.Println(links, err)
func Size(rect image.Rectangle) (width, height int)
func Split(path string) (dir, file string)
func HourMinSec(t time.Time) (hour, minute, second int)
package main import ( "fmt" "golang.org/x/net/html" "net/http" "os" "strings" ) func main() { words, images, _ := CountWordsAndImages(os.Args[1]) fmt.Printf("文字:%d,圖片:%d n", words, images) } // CountWordsAndImages 傳送一個 HTTP GET 請求,並且獲取文件的 // 字數與圖片數量 func CountWordsAndImages(url string) (words, images int, err error) { resp, err := http.Get(url) if err != nil { return } doc, err := html.Parse(resp.Body) resp.Body.Close() if err != nil { err = fmt.Errorf("parsing HTML: %s", err) return } words, images = countWordsAndImages(doc) //bare return return } func countWordsAndImages(n *html.Node) (words, images int) { texts, images := visit3(nil, 0, n) for _, v := range texts { v = strings.Trim(strings.TrimSpace(v), "rn") if v == "" { continue } words += strings.Count(v, "") } //bare return return } //遞迴迴圈html func visit3(texts []string, imgs int, n *html.Node) ([]string, int) { //文字 if n.Type == html.TextNode { texts = append(texts, n.Data) } //圖片 if n.Type == html.ElementNode && (n.Data == "img") { imgs++ } for c := n.FirstChild; c != nil; c = c.NextSibling { if c.Data == "script" || c.Data == "style" { continue } texts, imgs = visit3(texts, imgs, c) } //多返回值 return texts, imgs }裸返回是將每個命名返回結果按照順序返回的快捷方法,所以在上面的函數中,每個 return 語句都等同於:
return words, images, err
函數中存在多個返回語句且有多個返回結果時,裸返回可以消除重複程式碼,但是並不能使程式碼更加易於理解,對於這種方式,在第一眼看來,不能直觀地看出 return 語句返回的具體結果,鑑於這個原因,應保守使用裸返回。