2016-05-30 107 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