2016-05-30 85 views
2

我試圖實現我自己的beanstalkd客戶端作爲一種學習方式。 https://github.com/kr/beanstalkd/blob/master/doc/protocol.txtgolang-bufio閱讀多行直到(CRLF) r n定界符

目前,我使用bufio來讀取由\n分隔的一行數據。

res, err := this.reader.ReadLine('\n')

這是罰款當我發送一個命令,讀喜歡AA制單行響應:INSERTED %d\r\n但我覺得困難,當我嘗試保留一份工作,因爲工作機構可以是多行和這樣,我不能使用\n分隔符。

有沒有辦法讀入緩衝區,直到CRLF

例如當我發送reserve命令。我預期的響應如下:

RESERVED <id> <bytes>\r\n 
<data>\r\n 

但數據可能包含\n,所以我需要閱讀,直到\r\n

另外 - 有沒有一種方法可以讀取<bytes>中指定的特定字節數,如上面的示例響應所示?

目前,我有(處理刪除錯誤):

func (this *Bean) receiveLine() (string, error) { 
    res, err := this.reader.ReadString('\n') 
    return res, err 
} 

func (this *Bean) receiveBody(numBytesToRead int) ([]byte, error) { 
    res, err := this.reader.ReadString('\r\n') // What to do here to read to CRLF/up to number of expected bytes? 

    return res, err 
} 

func (this *Bean) Reserve() (*Job, error) { 

    this.send("reserve\r\n") 
    res, err := this.receiveLine() 

    var jobId uint64 
    var bodylen int 
    _, err = fmt.Sscanf(res, "RESERVED %d %d\r\n", &jobId, &bodylen) 

    body, err := this.receiveBody(bodylen) 

    job := new(Job) 
    job.Id = jobId 
    job.Body = body 

    return job, nil 
} 

回答

6

資源,ERR:= this.reader.Read( '\ n')

不使對我來說任何意義。你的意思是ReadBytes/ReadSlice/ReadString?

您需要bufio.Scanner。

定義您的bufio.SplitFunc(示例是bufio.ScanLines的一個副本,可修改爲查找'\ r \ n')。修改它以匹配您的案例。現在

// dropCR drops a terminal \r from the data. 
func dropCR(data []byte) []byte { 
    if len(data) > 0 && data[len(data)-1] == '\r' { 
     return data[0 : len(data)-1] 
    } 
    return data 
} 


func ScanCRLF(data []byte, atEOF bool) (advance int, token []byte, err error) { 
     if atEOF && len(data) == 0 { 
      return 0, nil, nil 
     } 
     if i := bytes.Index(data, []byte{'\r','\n'}); i >= 0 { 
      // We have a full newline-terminated line. 
      return i + 2, dropCR(data[0:i]), nil 
     } 
     // If we're at EOF, we have a final, non-terminated line. Return it. 
     if atEOF { 
      return len(data), dropCR(data), nil 
     } 
     // Request more data. 
     return 0, nil, nil 
    } 

,包住io.Reader與您的自定義掃描儀。

scanner := bufio.NewScanner(this.reader) 
scanner.Split(ScanCRLF) 
// Set the split function for the scanning operation. 
scanner.Split(split) 
// Validate the input 
for scanner.Scan() { 
     fmt.Printf("%s\n", scanner.Text()) 
} 

if err := scanner.Err(); err != nil { 
     fmt.Printf("Invalid input: %s", err) 
} 

閱讀bufio package's關於Scanner的源代碼。

另外 - 有沒有一種方法來讀取上面的示例響應中指定的特定字節數?

首先您需要閱讀「RESERVED \ r \ n」行中的一些內容。

然後你就可以使用

nr_of_bytes : = read_number_of_butes_somehow(this.reader) 
buf : = make([]byte, nr_of_bytes) 
this.reader.Read(buf) 

LimitedReader

但我不喜歡這種方法。

感謝這位讀者。閱讀('\ n')是一個錯字 - 我改正了的問題。我還附上了迄今爲止我所掌握的示例代碼。正如你所看到的,我可以得到正文的期望字節數。你能否詳細說明爲什麼你不喜歡閱讀特定字節數的想法?這似乎最合乎邏輯?

我想看看Bean的定義,特別是讀者的一部分。想象一下,這個計數器在某種程度上是錯誤的。

  1. 其短:你需要找到以下「\ r \ n」並放棄到目前爲止的一切?或不?那麼爲什麼你首先需要櫃檯呢?

  2. 它更大,那麼它應該是(或更糟糕的是它的巨大!)。

    2.1閱讀器中沒有下一條消息:好,閱讀時間短但預期好。

    2.2有下一條消息在等待:嗯,你讀了它的一部分,沒有簡單的方法來恢復。

    2.3其巨大:即使消息只有1個字節,也不能分配內存。

該字節計數器通常用於驗證消息。 看起來就像是使用beanstalkd協議的情況。

使用掃描儀,分析信息,檢查長度預計數...利潤

UPD

被警告,默認bufio.Scanner不能閱讀更多然後64K,設置最大長度scanner.Buffer第一。這很糟糕,因爲你不能即時改變這個選項,有些數據可能已被「預先」讀取 - 被掃描器讀取。

UPD2

我的最後一次更新的思考。看看net.textproto它是如何實現像簡單狀態機的dotReader的。您可以通過首先閱讀命令和「預期字節」檢查有效負載來做類似的事情。

+0

感謝此 - 'reader.Read('\ n')'是一個錯字 - 我更正了一個問題。我還附上了迄今爲止我所掌握的示例代碼。正如你所看到的,我可以得到正文的期望字節數。你能否詳細說明爲什麼你不喜歡閱讀特定字節數的想法?這似乎最合乎邏輯? – Gravy

+0

在帖子中添加了答案。 – Darigaaz