2016-07-26 1 views
0

Существует экскурсия. Я решил https://tour.golang.org/methods/23 так:Что не так с решением 23-й задачи тура?

func (old_reader rot13Reader) Read(b []byte) (int, error) { 
    const LEN int = 1024 
    tmp_bytes := make([]byte, LEN) 
    old_len, err := old_reader.r.Read(tmp_bytes) 
    if err == nil { 
     tmp_bytes = tmp_bytes[:old_len] 
     rot13(tmp_bytes) 
     return len(tmp_bytes), nil 
    } else { 
     return 0, err 
    } 
} 

func main() { 
    s := strings.NewReader("Lbh penpxrq gur pbqr!") 
    r := rot13Reader{s} 
    io.Copy(os.Stdout, &r) 
} 

Где rot13 правильно и отлаживать выход перед возвращение показывает правильную строку. Но почему нет вывода на консоль?

ответ

3

Метод Read для io.Reader должен работать на предоставленном ему барабане. Вы читаете новый фрагмент и никогда не изменяете оригинал.

Просто используйте b на протяжении метода чтения:

func (old_reader rot13Reader) Read(b []byte) (int, error) { 
    n, err := old_reader.r.Read(b) 
    rot13(b[:n]) 
    return n, err 
} 
+0

Спасибо! Выходные параметры - злые :( – devmeow

+0

Это очень сложно использовать предварительно выделенный буфер чтения, если вы не передадите его в качестве параметра. – JimB

+0

Этот код не совсем прав (хотя он будет работать для ' strings.Reader') - 'Read' может возвращать ошибку и ненулевое' n', а когда этот декодер не будет декодировать результат. 'n, err: = old_reader.r.Read (b) ; rot13 (b [: n]); return n, err' является более точным. –

1

Вы никогда не модифицируя b в читателе. Смысл функции io.ReaderRead состоит в том, что вы помещаете данные непосредственно в базовый массив b.

Считая функцию rot13() также в месте модифицирует, это будет работать (редактировать:. Я пытался сохранить этот код близко к версии, так что вы можете увидеть, что изменилось Простейшее решения JimB является более идиоматическим решением эта проблема):

func (old_reader rot13Reader) Read(b []byte) (int, error) { 
    tmp_bytes := make([]byte, len(b)) 
    old_len, err := old_reader.r.Read(tmp_bytes) 
    tmp_bytes = tmp_bytes[:old_len] 
    rot13(tmp_bytes) 
    for i := range tmp_bytes { 
     b[i] = tmp_bytes[i] 
    } 
    return old_len, err 
} 

Пример (с загасил rot13()): https://play.golang.org/p/vlbra-46zk

на стороне записки, из идиоматической Perspect, old_reader это не имя собственное приемника (равно как и в old_len правильное имя переменной). Go предпочитает короткие имена приемников (например, r или rdr в этом случае), а также предпочитайте верблюд для подчеркивания (символы подчеркивания на самом деле срабатывают предупреждение golint).

Редактировать2: Более идиоматическая версия вашего кода. Удержал тот же механизм действия, немного почистил его.

func (rdr rot13Reader) Read(b []byte) (int, error) { 
    tmp := make([]byte, len(b)) 
    n, err := rdr.r.Read(tmp) 
    tmp = tmp[:n] 
    rot13(tmp) 
    for i := range tmp { 
     b[i] = tmp[i] 
    } 
    return n, err 
} 

Исходя из этого, удаляя кусочек tmp байтов и с помощью назначения b непосредственно приводит к идиоматических решения JimB к проблеме.

Редактировать3: Обновлено, чтобы исправить проблему. Павел указал в комментариях.

+0

Вы должны использовать шаблон раннего возврата, чтобы удалить' els e' и отступ основного пути кода. Нет необходимости выделять дополнительный фрагмент, используя предоставленный срез.Если вам нужно скопировать, используйте встроенную копию 'copy' для копирования байтов, а не for-loop. – JimB

+0

Да, как я уже сказал в редакции, я намеренно старался сохранить изменения в своем коде до минимума, необходимого для его функциональности, поэтому различия легче увидеть. Это очень не идеальный идиоматический Go. – Kaedys

+0

Этот код имеет аналогичную проблему с JimB: 'Read' может возвращать n> 0 и err! = Nil (например: err = io.EOF), и когда это произойдет, вы обрезаете результат. –

Смежные вопросы