ch7: fix code format

pull/1/head
chai2010 2016-01-21 10:22:10 +08:00
parent 2420954025
commit 0b5ec941ed
13 changed files with 208 additions and 186 deletions

View File

@ -8,6 +8,7 @@
``` go
package fmt
func Fprintf(w io.Writer, format string, args ...interface{}) (int, error)
func Printf(format string, args ...interface{}) (int, error) {
return Fprintf(os.Stdout, format, args...)
@ -26,6 +27,7 @@ Fprintf的前綴F表示文件(File)也表明格式化輸出結果應該被寫入
``` go
package io
// Writer is the interface that wraps the basic Write method.
type Writer interface {
// Write writes len(p) bytes from p to the underlying data stream.
@ -45,21 +47,23 @@ io.Writer類型定義了函數Fprintf和這個函數調用者之間的約定。
讓我們通過一個新的類型來進行校驗,下面\*ByteCounter類型里的Write方法僅僅在丟失寫向它的字節前統計它們的長度。(在這個+=賦值語句中讓len(p)的類型和\*c的類型匹配的轉換是必須的。)
<u><i>gopl.io/ch7/bytecounter</i></u>
```go
// gopl.io/ch7/bytecounter
type ByteCounter int
func (c *ByteCounter) Write(p []byte) (int, error) {
*c += ByteCounter(len(p)) // convert int to ByteCounter
return len(p), nil
*c += ByteCounter(len(p)) // convert int to ByteCounter
return len(p), nil
}
```
因爲*ByteCounter滿足io.Writer的約定我們可以把它傳入Fprintf函數中Fprintf函數執行字符串格式化的過程不會去關註ByteCounter正確的纍加結果的長度。
```go
var c ByteCounter
c.Write([]byte("hello"))
fmt.Println(c) // "5", = len("hello")
c = 0 // reset the counter
c = 0 // reset the counter
var name = "Dolly"
fmt.Fprintf(&c, "hello, %s", name)
fmt.Println(c) // "12", = len("hello, Dolly")
@ -69,11 +73,12 @@ fmt.Println(c) // "12", = len("hello, Dolly")
```go
package fmt
// The String method is used to print values passed
// as an operand to any format that accepts a string
// or to an unformatted printer such as Print.
type Stringer interface {
String() string
String() string
}
```

View File

@ -1,4 +1,5 @@
## 7.2. 接口類型
接口類型具體描述了一繫列方法的集合,一個實現了這些方法的具體類型是這個接口類型的實例。
io.Writer類型是用的最廣泛的接口之一因爲它提供了所有的類型寫入bytes的抽象包括文件類型內存緩衝區網絡鏈接HTTP客戶端壓縮工具哈希等等。io包中定義了很多其它有用的接口類型。Reader可以代表任意可以讀取bytes的類型Closer可以是任意可以關閉的值例如一個文件或是網絡鏈接。到現在你可能註意到了很多Go語言中單方法接口的命名習慣
@ -6,10 +7,10 @@ io.Writer類型是用的最廣泛的接口之一因爲它提供了所有的
```go
package io
type Reader interface {
Read(p []byte) (n int, err error)
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
Close() error
}
```
@ -17,21 +18,21 @@ type Closer interface {
```go
type ReadWriter interface {
Reader
Writer
Reader
Writer
}
type ReadWriteCloser interface {
Reader
Writer
Closer
Reader
Writer
Closer
}
```
上面用到的語法和結構內嵌相似我們可以用這種方式以一個簡寫命名另一個接口而不用聲明它所有的方法。這種方式本稱爲接口內嵌。盡管略失簡潔我們可以像下面這樣不使用內嵌來聲明io.Writer接口。
```go
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
```
@ -39,8 +40,8 @@ type ReadWriter interface {
```go
type ReadWriter interface {
Read(p []byte) (n int, err error)
Writer
Read(p []byte) (n int, err error)
Writer
}
```

View File

@ -1,4 +1,5 @@
## 7.3. 實現接口的條件
一個類型如果擁有一個接口需要的所有方法,那麽這個類型就實現了這個接口。例如,\*os.File類型實現了io.ReaderWriterCloser和ReadWriter接口。\*bytes.Buffer實現了ReaderWriter和ReadWriter這些接口但是它沒有實現Closer接口因爲它不具有Close方法。Go的程序員經常會簡要的把一個具體的類型描述成一個特定的接口類型。舉個例子\*bytes.Buffer是io.Writer\*os.Files是io.ReadWriter。
接口指定的規則非常簡單:表達一個類型屬於某個接口隻要這個類型實現這個接口。所以:
@ -112,29 +113,29 @@ Track
```go
type Artifact interface {
Title() string
Creators() []string
Created() time.Time
Title() string
Creators() []string
Created() time.Time
}
```
其它的一些特性隻對特定類型的文化産品才有。和文字排版特性相關的隻有books和magazines還有隻有movies和TV劇集和屏幕分辨率相關。
```go
type Text interface {
Pages() int
Words() int
PageSize() int
Pages() int
Words() int
PageSize() int
}
type Audio interface {
Stream() (io.ReadCloser, error)
RunningTime() time.Duration
Format() string // e.g., "MP3", "WAV"
Stream() (io.ReadCloser, error)
RunningTime() time.Duration
Format() string // e.g., "MP3", "WAV"
}
type Video interface {
Stream() (io.ReadCloser, error)
RunningTime() time.Duration
Format() string // e.g., "MP4", "WMV"
Resolution() (x, y int)
Stream() (io.ReadCloser, error)
RunningTime() time.Duration
Format() string // e.g., "MP4", "WMV"
Resolution() (x, y int)
}
```
@ -142,9 +143,9 @@ type Video interface {
```go
type Streamer interface {
Stream() (io.ReadCloser, error)
RunningTime() time.Duration
Format() string
Stream() (io.ReadCloser, error)
RunningTime() time.Duration
Format() string
}
```

View File

@ -2,8 +2,8 @@
在本章我們會學到另一個標準的接口類型flag.Value是怎麽幫助命令行標記定義新的符號的。思考下面這個會休眠特定時間的程序
<u></i>gopl.io/ch7/sleep</i></u>
```go
// gopl.io/ch7/sleep
var period = flag.Duration("period", 1*time.Second, "sleep period")
func main() {
@ -51,8 +51,8 @@ String方法格式化標記的值用在命令行幫組消息中這樣每一
讓我們定義一個允許通過攝氏度或者華氏溫度變換的形式指定溫度的celsiusFlag類型。註意celsiusFlag內嵌了一個Celsius類型(§2.5)因此不用實現本身就已經有String方法了。爲了實現flag.Value我們隻需要定義Set方法
<u><i>gopl.io/ch7/tempconv</i></u>
```go
// gopl.io/ch7/tempconv
// *celsiusFlag satisfies the flag.Value interface.
type celsiusFlag struct{ Celsius }
@ -89,8 +89,8 @@ func CelsiusFlag(name string, value Celsius, usage string) *Celsius {
現在我們可以開始在我們的程序中使用新的標記:
<u><i>gopl.io/ch7/tempflag</i></u>
```go
// gopl.io/ch7/tempflag
var temp = tempconv.CelsiusFlag("temp", 20.0, "the temperature")
func main() {

View File

@ -8,22 +8,22 @@
const debug = true
func main() {
var buf *bytes.Buffer
if debug {
buf = new(bytes.Buffer) // enable collection of output
}
f(buf) // NOTE: subtly incorrect!
if debug {
// ...use buf...
}
var buf *bytes.Buffer
if debug {
buf = new(bytes.Buffer) // enable collection of output
}
f(buf) // NOTE: subtly incorrect!
if debug {
// ...use buf...
}
}
// If out is non-nil, output will be written to it.
func f(out io.Writer) {
// ...do something...
if out != nil {
out.Write([]byte("done!\n"))
}
// ...do something...
if out != nil {
out.Write([]byte("done!\n"))
}
}
```
@ -31,7 +31,7 @@ func f(out io.Writer) {
```go
if out != nil {
out.Write([]byte("done!\n")) // panic: nil pointer dereference
out.Write([]byte("done!\n")) // panic: nil pointer dereference
}
```
@ -46,7 +46,7 @@ if out != nil {
```go
var buf io.Writer
if debug {
buf = new(bytes.Buffer) // enable collection of output
buf = new(bytes.Buffer) // enable collection of output
}
f(buf) // OK
```

View File

@ -29,6 +29,7 @@ func (p StringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
```go
sort.Sort(StringSlice(names))
```
這個轉換得到一個相同長度容量和基於names數組的切片值併且這個切片值的類型有三個排序需要的方法。
對字符串切片的排序是很常用的需要所以sort包提供了StringSlice類型也提供了Strings函數能讓上面這些調用簡化成sort.Strings(names)。
@ -39,8 +40,8 @@ sort.Sort(StringSlice(names))
下面的變量tracks包好了一個播放列表。One of the authors apologizes for the other authors musical tastes.每個元素都不是Track本身而是指向它的指針。盡管我們在下面的代碼中直接存儲Tracks也可以工作sort函數會交換很多對元素所以如果每個元素都是指針會更快而不是全部Track類型指針是一個機器字碼長度而Track類型可能是八個或更多。
<u><i>gopl.io/ch7/sorting</i></u>
```go
//gopl.io/ch7/sorting
type Track struct {
Title string
Artist string
@ -210,6 +211,7 @@ sort.Sort(sort.Reverse(sort.IntSlice(values)))
fmt.Println(values) // "[4 3 1 1]"
fmt.Println(sort.IntsAreSorted(values)) // "false"
```
爲了使用方便sort包爲[]int,[]string和[]float64的正常排序提供了特定版本的函數和類型。對於其他類型例如[]int64或者[]uint盡管路徑也很簡單還是依賴我們自己實現。
**練習 7.8** 很多圖形界面提供了一個有狀態的多重排序表格插件主要的排序鍵是最近一次點擊過列頭的列第二個排序鍵是第二最近點擊過列頭的列等等。定義一個sort.Interface的實現用在這樣的表格中。比較這個實現方式和重複使用sort.Stable來排序的方式。

View File

@ -2,8 +2,8 @@
在第一章中我們粗略的了解了怎麽用net/http包去實現網絡客戶端(§1.5)和服務器(§1.7)。在這個小節中我們會對那些基於http.Handler接口的服務器API做更進一步的學習
<u><i>net/http</i></u>
```go
net/http
package http
type Handler interface {
@ -17,8 +17,8 @@ ListenAndServe函數需要一個例如“localhost:8000”的服務器地址
想象一個電子商務網站爲了銷售它的數據庫將它物品的價格映射成美元。下面這個程序可能是能想到的最簡單的實現了。它將庫存清單模型化爲一個命名爲database的map類型我們給這個類型一個ServeHttp方法這樣它可以滿足http.Handler接口。這個handler會遍歷整個map併輸出物品信息。
<u><i>gopl.io/ch7/http1</i></u>
```go
gopl.io/ch7/http1
func main() {
db := database{"shoes": 50, "socks": 5}
log.Fatal(http.ListenAndServe("localhost:8000", db))
@ -55,8 +55,8 @@ socks: $5.00
目前爲止這個服務器不考慮URL隻能爲每個請求列出它全部的庫存清單。更眞實的服務器會定義多個不同的URL每一個都會觸發一個不同的行爲。讓我們使用/list來調用已經存在的這個行爲併且增加另一個/price調用表明單個貨品的價格像這樣/price?item=socks來指定一個請求參數。
<u><i>gopl.io/ch7/http2</i></u>
```go
gopl.io/ch7/http2
func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/list":
@ -114,8 +114,8 @@ no such page: /help
在下面的程序中我們創建一個ServeMux併且使用它將URL和相應處理/list和/price操作的handler聯繫起來這些操作邏輯都已經被分到不同的方法中。然後我門在調用ListenAndServe函數中使用ServeMux最爲主要的handler。
<u><i>gopl.io/ch7/http3</i></u>
```go
gopl.io/ch7/http3
func main() {
db := database{"shoes": 50, "socks": 5}
mux := http.NewServeMux()
@ -154,8 +154,8 @@ func(w http.ResponseWriter, req *http.Request)
語句http.HandlerFunc(db.list)是一個轉換而非一個函數調用因爲http.HandlerFunc是一個類型。它有如下的定義
<u><i>net/http</i></u>
```go
net/http
package http
type HandlerFunc func(w ResponseWriter, r *Request)
@ -169,8 +169,8 @@ HandlerFunc顯示了在Go語言接口機製中一些不同尋常的特點。這
因爲handler通過這種方式註冊非常普遍ServeMux有一個方便的HandleFunc方法它幫我們簡化handler註冊代碼成這樣
<u><i>gopl.io/ch7/http3a</i></u>
```go
gopl.io/ch7/http3a
mux.HandleFunc("/list", db.list)
mux.HandleFunc("/price", db.price)
```
@ -181,8 +181,8 @@ mux.HandleFunc("/price", db.price)
然後服務器的主函數可以簡化成:
<u><i>gopl.io/ch7/http4</i></u>
```go
gopl.io/ch7/http4
func main() {
db := database{"shoes": 50, "socks": 5}
http.HandleFunc("/list", db.list)

View File

@ -34,7 +34,7 @@ package fmt
import "errors"
func Errorf(format string, args ...interface{}) error {
return errors.New(Sprintf(format, args...))
return errors.New(Sprintf(format, args...))
}
```

View File

@ -17,8 +17,8 @@ pow(x, 3) + pow(y, 3)
下面的五個具體類型表示了具體的表達式類型。Var類型表示對一個變量的引用。我們很快會知道爲什麽它可以被輸出。literal類型表示一個浮點型常量。unary和binary類型表示有一到兩個運算對象的運算符表達式這些操作數可以是任意的Expr類型。call類型表示對一個函數的調用我們限製它的fn字段隻能是powsin或者sqrt。
<u><i>gopl.io/ch7/eval</i></u>
```go
gopl.io/ch7/eval
// A Var identifies a variable, e.g., x.
type Var string
@ -254,8 +254,8 @@ Check方法的參數是一個Var類型的集合這個集合聚集從表達式
這個ParseAndCheck函數混合了解析和檢査步驟的過程
<u><i>gopl.io/ch7/surface</i></u>
```go
gopl.io/ch7/surface
import "gopl.io/ch7/eval"
func parseAndCheck(s string) (eval.Expr, error) {

View File

@ -1,5 +1,7 @@
## 7.11. 基於類型斷言區别錯誤類型
思考在os包中文件操作返迴的錯誤集合。I/O可以因爲任何數量的原因失敗但是有三種經常的錯誤必須進行不同的處理文件已經存在對於創建操作找不到文件對於讀取操作和權限拒絶。os包中提供了這三個幫助函數來對給定的錯誤值表示的失敗進行分類
```go
package os
@ -7,31 +9,37 @@ func IsExist(err error) bool
func IsNotExist(err error) bool
func IsPermission(err error) bool
```
對這些判斷的一個缺乏經驗的實現可能會去檢査錯誤消息是否包含了特定的子字符串,
```go
func IsNotExist(err error) bool {
// NOTE: not robust!
return strings.Contains(err.Error(), "file does not exist")
// NOTE: not robust!
return strings.Contains(err.Error(), "file does not exist")
}
```
但是處理I/O錯誤的邏輯可能一個和另一個平台非常的不同所以這種方案併不健壯併且對相同的失敗可能會報出各種不同的錯誤消息。在測試的過程中通過檢査錯誤消息的子字符串來保證特定的函數以期望的方式失敗是非常有用的但對於線上的代碼是不夠的。
一個更可靠的方式是使用一個專門的類型來描述結構化的錯誤。os包中定義了一個PathError類型來描述在文件路徑操作中涉及到的失敗像Open或者Delete操作,併且定義了一個叫LinkError的變體來描述涉及到兩個文件路徑的操作像Symlink和Rename。這下面是os.PathError
```go
package os
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
Path string
Err error
Op string
Path string
Err error
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
```
大多數調用方都不知道PathError併且通過調用錯誤本身的Error方法來統一處理所有的錯誤。盡管PathError的Error方法簡單地把這些字段連接起來生成錯誤消息PathError的結構保護了內部的錯誤組件。調用方需要使用類型斷言來檢測錯誤的具體類型以便將一種失敗和另一種區分開具體的類型比字符串可以提供更多的細節。
```go
_, err := os.Open("/no/such/file")
fmt.Println(err) // "open /no/such/file: No such file or directory"
@ -39,11 +47,13 @@ fmt.Printf("%#v\n", err)
// Output:
// &os.PathError{Op:"open", Path:"/no/such/file", Err:0x2}
```
這就是三個幫助函數是怎麽工作的。例如下面展示的IsNotExist它會報出是否一個錯誤和syscall.ENOENT(§7.8)或者和有名的錯誤os.ErrNotExist相等(可以在§5.4.2中找到io.EOF或者是一個*PathError它內部的錯誤是syscall.ENOENT和os.ErrNotExist其中之一。
```go
import (
"errors"
"syscall"
"errors"
"syscall"
)
var ErrNotExist = errors.New("file does not exist")
@ -52,15 +62,18 @@ var ErrNotExist = errors.New("file does not exist")
// report that a file or directory does not exist. It is satisfied by
// ErrNotExist as well as some syscall errors.
func IsNotExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
return err == syscall.ENOENT || err == ErrNotExist
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
return err == syscall.ENOENT || err == ErrNotExist
}
```
下面這里是它的實際使用:
```go
_, err := os.Open("/no/such/file")
fmt.Println(os.IsNotExist(err)) // "true"
```
如果錯誤消息結合成一個更大的字符串當然PathError的結構就不再爲人所知例如通過一個對fmt.Errorf函數的調用。區别錯誤通常必須在失敗操作後錯誤傳迴調用者前進行。

View File

@ -4,13 +4,13 @@
```go
func writeHeader(w io.Writer, contentType string) error {
if _, err := w.Write([]byte("Content-Type: ")); err != nil {
return err
}
if _, err := w.Write([]byte(contentType)); err != nil {
return err
}
// ...
if _, err := w.Write([]byte("Content-Type: ")); err != nil {
return err
}
if _, err := w.Write([]byte(contentType)); err != nil {
return err
}
// ...
}
```
@ -24,23 +24,23 @@ func writeHeader(w io.Writer, contentType string) error {
// writeString writes s to w.
// If w has a WriteString method, it is invoked instead of w.Write.
func writeString(w io.Writer, s string) (n int, err error) {
type stringWriter interface {
WriteString(string) (n int, err error)
}
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s) // avoid a copy
}
return w.Write([]byte(s)) // allocate temporary copy
type stringWriter interface {
WriteString(string) (n int, err error)
}
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s) // avoid a copy
}
return w.Write([]byte(s)) // allocate temporary copy
}
func writeHeader(w io.Writer, contentType string) error {
if _, err := writeString(w, "Content-Type: "); err != nil {
return err
}
if _, err := writeString(w, contentType); err != nil {
return err
}
// ...
if _, err := writeString(w, "Content-Type: "); err != nil {
return err
}
if _, err := writeString(w, contentType); err != nil {
return err
}
// ...
}
```
@ -50,8 +50,8 @@ func writeHeader(w io.Writer, contentType string) error {
```go
interface {
io.Writer
WriteString(s string) (n int, err error)
io.Writer
WriteString(s string) (n int, err error)
}
```
@ -65,13 +65,13 @@ interface {
package fmt
func formatOneValue(x interface{}) string {
if err, ok := x.(error); ok {
return err.Error()
}
if str, ok := x.(Stringer); ok {
return str.String()
}
// ...all other types...
if err, ok := x.(error); ok {
return err.Error()
}
if str, ok := x.(Stringer); ok {
return str.String()
}
// ...all other types...
}
```

View File

@ -12,10 +12,10 @@
import "database/sql"
func listTracks(db sql.DB, artist string, minYear, maxYear int) {
result, err := db.Exec(
"SELECT * FROM tracks WHERE artist = ? AND ? <= year AND year <= ?",
artist, minYear, maxYear)
// ...
result, err := db.Exec(
"SELECT * FROM tracks WHERE artist = ? AND ? <= year AND year <= ?",
artist, minYear, maxYear)
// ...
}
```
@ -23,22 +23,22 @@ Exec方法使用SQL字面量替換在査詢字符串中的每個'?'SQL字面
```go
func sqlQuote(x interface{}) string {
if x == nil {
return "NULL"
} else if _, ok := x.(int); ok {
return fmt.Sprintf("%d", x)
} else if _, ok := x.(uint); ok {
return fmt.Sprintf("%d", x)
} else if b, ok := x.(bool); ok {
if b {
return "TRUE"
}
return "FALSE"
} else if s, ok := x.(string); ok {
return sqlQuoteString(s) // (not shown)
} else {
panic(fmt.Sprintf("unexpected type %T: %v", x, x))
}
if x == nil {
return "NULL"
} else if _, ok := x.(int); ok {
return fmt.Sprintf("%d", x)
} else if _, ok := x.(uint); ok {
return fmt.Sprintf("%d", x)
} else if b, ok := x.(bool); ok {
if b {
return "TRUE"
}
return "FALSE"
} else if s, ok := x.(string); ok {
return sqlQuoteString(s) // (not shown)
} else {
panic(fmt.Sprintf("unexpected type %T: %v", x, x))
}
}
```
@ -48,11 +48,11 @@ switch語句可以簡化if-else鏈如果這個if-else鏈對一連串值做相
```go
switch x.(type) {
case nil: // ...
case int, uint: // ...
case bool: // ...
case string: // ...
default: // ...
case nil: // ...
case int, uint: // ...
case bool: // ...
case string: // ...
default: // ...
}
```
@ -70,21 +70,21 @@ switch x := x.(type) { /* ... */ }
```go
func sqlQuote(x interface{}) string {
switch x := x.(type) {
case nil:
return "NULL"
case int, uint:
return fmt.Sprintf("%d", x) // x has type interface{} here.
case bool:
if x {
return "TRUE"
}
return "FALSE"
case string:
return sqlQuoteString(x) // (not shown)
default:
panic(fmt.Sprintf("unexpected type %T: %v", x, x))
}
switch x := x.(type) {
case nil:
return "NULL"
case int, uint:
return fmt.Sprintf("%d", x) // x has type interface{} here.
case bool:
if x {
return "TRUE"
}
return "FALSE"
case string:
return sqlQuoteString(x) // (not shown)
default:
panic(fmt.Sprintf("unexpected type %T: %v", x, x))
}
}
```

View File

@ -4,17 +4,17 @@
這里顯示的是和這個API相關的部分
<u><i>encoding/xml</i></u>
```go
encoding/xml
package xml
type Name struct {
Local string // e.g., "Title" or "id"
Local string // e.g., "Title" or "id"
}
type Attr struct { // e.g., name="value"
Name Name
Value string
Name Name
Value string
}
// A Token includes StartElement, EndElement, CharData,
@ -37,55 +37,55 @@ func (*Decoder) Token() (Token, error) // returns next Token in sequence
下面的xmlselect程序獲取和打印在一個XML文檔樹中確定的元素下找到的文本。使用上面的API它可以在輸入上一次完成它的工作而從來不要具體化這個文檔樹。
<u><i>gopl.io/ch7/xmlselect</i></u>
```go
gopl.io/ch7/xmlselect
// Xmlselect prints the text of selected elements of an XML document.
package main
import (
"encoding/xml"
"fmt"
"io"
"os"
"strings"
"encoding/xml"
"fmt"
"io"
"os"
"strings"
)
func main() {
dec := xml.NewDecoder(os.Stdin)
var stack []string // stack of element names
for {
tok, err := dec.Token()
if err == io.EOF {
break
} else if err != nil {
fmt.Fprintf(os.Stderr, "xmlselect: %v\n", err)
os.Exit(1)
}
switch tok := tok.(type) {
case xml.StartElement:
stack = append(stack, tok.Name.Local) // push
case xml.EndElement:
stack = stack[:len(stack)-1] // pop
case xml.CharData:
if containsAll(stack, os.Args[1:]) {
fmt.Printf("%s: %s\n", strings.Join(stack, " "), tok)
}
}
}
dec := xml.NewDecoder(os.Stdin)
var stack []string // stack of element names
for {
tok, err := dec.Token()
if err == io.EOF {
break
} else if err != nil {
fmt.Fprintf(os.Stderr, "xmlselect: %v\n", err)
os.Exit(1)
}
switch tok := tok.(type) {
case xml.StartElement:
stack = append(stack, tok.Name.Local) // push
case xml.EndElement:
stack = stack[:len(stack)-1] // pop
case xml.CharData:
if containsAll(stack, os.Args[1:]) {
fmt.Printf("%s: %s\n", strings.Join(stack, " "), tok)
}
}
}
}
// containsAll reports whether x contains the elements of y, in order.
func containsAll(x, y []string) bool {
for len(y) <= len(x) {
if len(y) == 0 {
return true
}
if x[0] == y[0] {
y = y[1:]
}
x = x[1:]
}
return false
for len(y) <= len(x) {
if len(y) == 0 {
return true
}
if x[0] == y[0] {
y = y[1:]
}
x = x[1:]
}
return false
}
```
@ -122,8 +122,8 @@ type Node interface{} // CharData or *Element
type CharData string
type Element struct {
Type xml.Name
Attr []xml.Attr
Children []Node
Type xml.Name
Attr []xml.Attr
Children []Node
}
```