dongyouzhi7218 2016-05-30 16:47
浏览 230
已采纳

golang-bufio读取多行,直到(CRLF) 分隔符

I am trying to implement my own beanstalkd client as a way of learning go. https://github.com/kr/beanstalkd/blob/master/doc/protocol.txt

At the moment, I am using bufio to read in a line of data delimited by .

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

This is fine for when I send a single command, and read a a single line response like: INSERTED %d but I find difficulties when I try to reserve a job because the job body could be multiple lines and as such, I cannot use the delimiter.

Is there a way to read into the buffer until CRLF?

e.g. when I send the reserve command. My expected response is as follows:

RESERVED <id> <bytes>

<data>

But data could contain , so I need to read until the .

Alternatively - is there a way of reading a specific number of bytes as specified in <bytes> in example response above?

At the moment, I have (err handling removed):

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

func (this *Bean) receiveBody(numBytesToRead int) ([]byte, error) {
    res, err := this.reader.ReadString('
') // 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
")
    res, err := this.receiveLine()

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

    body, err := this.receiveBody(bodylen)

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

    return job, nil
}
  • 写回答

1条回答 默认 最新

  • dongma7796 2016-05-30 18:05
    关注

    res, err := this.reader.Read(' ')

    Does not make any sense to me. Did you mean ReadBytes/ReadSlice/ReadString?

    You need bufio.Scanner.

    Define your bufio.SplitFunc (example is a copy of bufio.ScanLines with modifications to look for ' '). Modify it to match your case.

    // dropCR drops a terminal  from the data.
    func dropCR(data []byte) []byte {
        if len(data) > 0 && data[len(data)-1] == '' {
            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{'','
    '}); 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
        }
    

    Now, wrap your io.Reader with your custom scanner.

    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
    ", scanner.Text())
    }
    
    if err := scanner.Err(); err != nil {
            fmt.Printf("Invalid input: %s", err)
    }
    

    Read bufio package's source code about Scanner.

    Alternatively - is there a way of reading a specific number of bytes as specified in in example response above?

    First you need to read "RESERVED " line some how.

    And then you can use

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

    or LimitedReader.

    But i dont like this approach.

    Thanks for this - reader.Read(' ') was a typo - I corrected question. I have also attached example code of where I have got so far. As you can see, I can get the number of expected bytes of the body. Could you elaborate on why you don't like the idea of reading a specific number of bytes? This seems most logical?

    I'd like to see Bean's definition, especially reader's part. Imagine, this counter is wrong somehow.

    1. Its short: you need to find following " " and discard everything up to that point? or not? why do you need counter in the first place then?

    2. Its bigger then it should be (or even worse its huge!).

      2.1 No next message in the reader: fine, read is shorter then expected but its fine.

      2.2 There is next message waiting: bah, you read part of it and there is no easy way to recover.

      2.3 Its huge: you cant allocate memory even if message is only 1 byte.

    This byte counters in general are designed to verify the message. And looks like it is the case with beanstalkd protocol.

    Use Scanner, parse message, check length with expected number ... profit

    UPD

    Be warned, default bufio.Scanner cant read more then 64k, set max length with scanner.Buffer first. And thats bad, because you cant change this option on the fly and some data may have had been "pre"-read by scanner.

    UPD2

    Thinking about my last update. Take a look at net.textproto how it implements dotReader like simple state machine. You could do something similar with reading command first and "expected bytes" checking on payload.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥60 pb数据库修改或者求完整pb库存系统,需为pb自带数据库
  • ¥15 spss统计中二分类变量和有序变量的相关性分析可以用kendall相关分析吗?
  • ¥15 拟通过pc下指令到安卓系统,如果追求响应速度,尽可能无延迟,是不是用安卓模拟器会优于实体的安卓手机?如果是,可以快多少毫秒?
  • ¥20 神经网络Sequential name=sequential, built=False
  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
  • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)
  • ¥15 相敏解调 matlab
  • ¥15 求lingo代码和思路