go-reader - 解答
実装コード
package reader
import (
"io"
"syscall"
)
const bufferSize = 1024
// fdBuffers は各fdのバッファを保持
var fdBuffers = make(map[int][]byte)
// GetNextLine はファイルディスクリプタから1行読み込む
func GetNextLine(fd int) (string, error) {
// バッファを取得または初期化
buffer, exists := fdBuffers[fd]
if !exists {
buffer = make([]byte, 0)
}
for {
// バッファ内で改行を探す
for i, b := range buffer {
if b == '\n' {
line := string(buffer[:i+1])
fdBuffers[fd] = buffer[i+1:]
return line, nil
}
}
// 新しいデータを読み込む
tempBuf := make([]byte, bufferSize)
n, err := syscall.Read(fd, tempBuf)
if n > 0 {
buffer = append(buffer, tempBuf[:n]...)
fdBuffers[fd] = buffer
}
if err != nil || n == 0 {
// EOF: 残りのバッファを返す
if len(buffer) > 0 {
line := string(buffer)
fdBuffers[fd] = nil
return line, nil
}
delete(fdBuffers, fd)
return "", io.EOF
}
}
}
// LineReader は io.Reader からの行読み込み
type LineReader struct {
reader io.Reader
buffer []byte
}
// NewLineReader は新しい LineReader を作成
func NewLineReader(r io.Reader) *LineReader {
return &LineReader{
reader: r,
buffer: make([]byte, 0),
}
}
// ReadLine は1行読み込む
func (lr *LineReader) ReadLine() (string, error) {
for {
// バッファ内で改行を探す
for i, b := range lr.buffer {
if b == '\n' {
line := string(lr.buffer[:i+1])
lr.buffer = lr.buffer[i+1:]
return line, nil
}
}
// 新しいデータを読み込む
tempBuf := make([]byte, bufferSize)
n, err := lr.reader.Read(tempBuf)
if n > 0 {
lr.buffer = append(lr.buffer, tempBuf[:n]...)
}
if err != nil {
if len(lr.buffer) > 0 {
line := string(lr.buffer)
lr.buffer = nil
return line, nil
}
return "", err
}
}
}
テストコード
package reader
import (
"io"
"strings"
"testing"
)
func TestLineReader(t *testing.T) {
input := "line1\nline2\nline3"
expected := []string{"line1\n", "line2\n", "line3"}
lr := NewLineReader(strings.NewReader(input))
for i, exp := range expected {
line, err := lr.ReadLine()
if i < len(expected)-1 && err != nil {
t.Errorf("Unexpected error: %v", err)
}
if line != exp {
t.Errorf("Line %d: got %q, want %q", i, line, exp)
}
}
_, err := lr.ReadLine()
if err != io.EOF {
t.Errorf("Expected EOF, got %v", err)
}
}