Я не могу найти file.ReadLineфункцию в Go. Я могу понять, как быстро написать один, но мне просто интересно, что я здесь пропускаю. Как читать файл построчно?
Обратите внимание, что если строка не помещается в буфер чтения, функция вернет неполную строку. Если вы хотите всегда читать всю строку в вашей программе одним вызовом функции, вам нужно будет инкапсулировать ReadLineфункцию в вашу собственную функцию, которая вызывает ReadLineцикл for.
bufio.ReadString('\n')не полностью эквивалентен, ReadLineпотому что ReadStringне может обработать случай, когда последняя строка файла не заканчивается символом новой строки.
Из документов: «ReadLine - это низкоуровневый примитив для чтения строк. Большинству абонентов следует использовать ReadBytes ('\ n') или ReadString ('\ n') или использовать сканер."
mdwhatcott
12
@mdwhatcott, почему так важно, что это "низкоуровневый примитив для чтения строк"? Как это приводит к выводу, что «Большинство вызывающих абонентов должны использовать вместо них ReadBytes ('\ n') или ReadString ('\ n') или использовать сканер."?
Чарли Паркер
12
@CharlieParker - Не уверен, просто цитируя документы, чтобы добавить контекст.
mdwhatcott
11
Из тех же документов ... "Если ReadString обнаруживает ошибку до нахождения разделителя, он возвращает данные, прочитанные до ошибки, и саму ошибку (часто io.EOF)." Таким образом, вы можете просто проверить на наличие ошибки io.EOF и знать, что все сделано.
eduncan911
1
Обратите внимание, что чтение или запись может завершиться ошибкой из-за прерванного системного вызова, что приводит к тому, что число читаемых или записываемых байтов меньше ожидаемого.
Джастин Суонхарт
599
В Go 1.1 и новее самый простой способ сделать это с помощью bufio.Scanner. Вот простой пример, который читает строки из файла:
Есть одно предупреждение: сканер плохо справляется со строками длиннее 65536 символов. Если это проблема для вас, тогда вам, вероятно, стоит свернуть свою собственную Reader.Read().
И поскольку ОП попросил просканировать файл, сначала было бы тривиально, file, _ := os.Open("/path/to/file.csv")а затем просканировал дескриптор файла:scanner := bufio.NewScanner(file)
Эван Пламли
14
Не забудьте defer file.Close().
Кирилл
13
Проблема в том, что Scanner.Scan () ограничен размером буфера в 4096 байт на строку. Вы получите bufio.ErrTooLongошибку, bufio.Scanner: token too longесли строка слишком длинная. В этом случае вам придется использовать bufio.ReaderLine () или ReadString ().
eduncan911
5
Просто мои 0,02 доллара - это самый правильный ответ на странице :)
Если вы не возражаете, что строка может быть очень длинной (то есть использовать много оперативной памяти). Он сохраняет \nв конце возвращаемой строки.
reader.ReadLine()
Если вы заботитесь об ограничении потребления ОЗУ и не возражаете против дополнительной работы со случаем, когда строка превышает размер буфера считывателя.
Я протестировал различные решения, предложенные при написании программы для тестирования сценариев, которые определены как проблемы в других ответах:
Файл со строкой 4 МБ.
Файл, который не заканчивается переводом строки.
Я нашел это:
ScannerРешение не обрабатывать длинные строки.
ReadLineРешение является сложным для реализации.
ReadStringРешение является наиболее простым и работает для длинных линий.
Вот код, который демонстрирует каждое решение, его можно запустить через go run main.go:
package main
import("bufio""bytes""fmt""io""os")
func readFileWithReadString(fn string)(err error){
fmt.Println("readFileWithReadString")
file, err := os.Open(fn)
defer file.Close()if err !=nil{return err
}// Start reading from the file with a reader.
reader := bufio.NewReader(file)var line stringfor{
line, err = reader.ReadString('\n')
fmt.Printf(" > Read %d characters\n", len(line))// Process the line here.
fmt.Println(" > > "+ limitLength(line,50))if err !=nil{break}}if err != io.EOF {
fmt.Printf(" > Failed!: %v\n", err)}return}
func readFileWithScanner(fn string)(err error){
fmt.Println("readFileWithScanner - this will fail!")// Don't use this, it doesn't work with long lines...
file, err := os.Open(fn)
defer file.Close()if err !=nil{return err
}// Start reading from the file using a scanner.
scanner := bufio.NewScanner(file)for scanner.Scan(){
line := scanner.Text()
fmt.Printf(" > Read %d characters\n", len(line))// Process the line here.
fmt.Println(" > > "+ limitLength(line,50))}if scanner.Err()!=nil{
fmt.Printf(" > Failed!: %v\n", scanner.Err())}return}
func readFileWithReadLine(fn string)(err error){
fmt.Println("readFileWithReadLine")
file, err := os.Open(fn)
defer file.Close()if err !=nil{return err
}// Start reading from the file with a reader.
reader := bufio.NewReader(file)for{var buffer bytes.Buffervar l []bytevar isPrefix boolfor{
l, isPrefix, err = reader.ReadLine()
buffer.Write(l)// If we've reached the end of the line, stop reading.if!isPrefix {break}// If we're just at the EOF, breakif err !=nil{break}}if err == io.EOF {break}
line := buffer.String()
fmt.Printf(" > Read %d characters\n", len(line))// Process the line here.
fmt.Println(" > > "+ limitLength(line,50))}if err != io.EOF {
fmt.Printf(" > Failed!: %v\n", err)}return}
func main(){
testLongLines()
testLinesThatDoNotFinishWithALinebreak()}
func testLongLines(){
fmt.Println("Long lines")
fmt.Println()
createFileWithLongLine("longline.txt")
readFileWithReadString("longline.txt")
fmt.Println()
readFileWithScanner("longline.txt")
fmt.Println()
readFileWithReadLine("longline.txt")
fmt.Println()}
func testLinesThatDoNotFinishWithALinebreak(){
fmt.Println("No linebreak")
fmt.Println()
createFileThatDoesNotEndWithALineBreak("nolinebreak.txt")
readFileWithReadString("nolinebreak.txt")
fmt.Println()
readFileWithScanner("nolinebreak.txt")
fmt.Println()
readFileWithReadLine("nolinebreak.txt")
fmt.Println()}
func createFileThatDoesNotEndWithALineBreak(fn string)(err error){
file, err := os.Create(fn)
defer file.Close()if err !=nil{return err
}
w := bufio.NewWriter(file)
w.WriteString("Does not end with linebreak.")
w.Flush()return}
func createFileWithLongLine(fn string)(err error){
file, err := os.Create(fn)
defer file.Close()if err !=nil{return err
}
w := bufio.NewWriter(file)
fs :=1024*1024*4// 4MB// Create a 4MB long line consisting of the letter a.for i :=0; i < fs; i++{
w.WriteRune('a')}// Terminate the line with a break.
w.WriteRune('\n')// Put in a second line, which doesn't have a linebreak.
w.WriteString("Second line.")
w.Flush()return}
func limitLength(s string, length int)string{if len(s)< length {return s
}return s[:length]}
Я тестировал на:
go версия go1.7 windows / amd64
go версия go1.6.3 linux / amd64
go версия go1.7.4 darwin / amd64
Результаты тестовой программы:
Long lines
readFileWithReadString
>Read4194305 characters
>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
>Read12 characters
>>Second line.
readFileWithScanner -this will fail!>Failed!: bufio.Scanner: token too long
readFileWithReadLine
>Read4194304 characters
>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
>Read12 characters
>>Second line.No linebreak
readFileWithReadString
>Read28 characters
>>Doesnotendwith linebreak.
readFileWithScanner -this will fail!>Read28 characters
>>Doesnotendwith linebreak.
readFileWithReadLine
>Read28 characters
>>Doesnotendwith linebreak.
Вы должны проверить ошибку должным образом, как показано в документах: play.golang.org/p/5CCPzVTSj6 т.е. если err == io.EOF {break} else {return err}
Chuque
53
РЕДАКТИРОВАТЬ: Начиная с go1.1, идиоматическое решение заключается в использовании bufio.Scanner
Я написал способ легко читать каждую строку из файла. Функция Readln (* bufio.Reader) возвращает строку (sans \ n) из базовой структуры bufio.Reader.
// Readln returns a single line (without the ending \n)// from the input buffered reader.// An error is returned iff there is an error with the// buffered reader.
func Readln(r *bufio.Reader)(string, error){var(isPrefix bool=true
err error =nil
line, ln []byte)for isPrefix && err ==nil{
line, isPrefix, err = r.ReadLine()
ln = append(ln, line...)}returnstring(ln),err
}
Вы можете использовать Readln для чтения каждой строки из файла. Следующий код читает каждую строку в файле и выводит каждую строку в стандартный вывод.
f, err := os.Open(fi)if err !=nil{
fmt.Printf("error opening file: %v\n",err)
os.Exit(1)}
r := bufio.NewReader(f)
s, e :=Readln(r)for e ==nil{
fmt.Println(s)
s,e =Readln(r)}
Я написал этот ответ до выхода Go 1.1. Go 1.1 имеет пакет Scanner в stdlib. это обеспечивает ту же функциональность, что и мой ответ. Я бы рекомендовал использовать Scanner вместо моего ответа, так как Scanner находится в stdlib. Счастливого взлома! :-)
Малькольм
30
Существует два распространенных способа чтения файла построчно.
Используйте bufio.Scanner
Используйте ReadString / ReadBytes / ... в bufio.Reader
Имейте в bufio.Readerвиду, что этот пример не будет читать последнюю строку в файле, если он не заканчивается новой строкой. ReadStringвернет как последнюю строку, так и io.EOFв этом случае.
но это дает ошибку, когда есть строка, которая больше, чем буфер сканера.
Когда это произошло, я использую reader := bufio.NewReader(inFile)create и concat моего собственного буфера, используя ch, err := reader.ReadByte()илиlen, err := reader.Read(myBuffer)
Другой способ, которым я пользуюсь (замените os.Stdin на файл, как указано выше), этот метод работает, когда строки длинные (isPrefix) и игнорирует пустые строки:
bufio.Reader.ReadLine () работает хорошо. Но если вы хотите прочитать каждую строку по строке, попробуйте использовать ReadString ('\ n') . Не нужно изобретать велосипед.
// strip '\n' or read until EOF, return error if read error
func readline(reader io.Reader)(line []byte, err error){
line = make([]byte,0,100)for{
b := make([]byte,1)
n, er := reader.Read(b)if n >0{
c := b[0]if c =='\n'{// end of line break}
line = append(line, c)}if er !=nil{
err = er
return}}return}
Мне нравится решение Lzap, я новичок в Go, я хотел бы попросить lzap, но я не смог этого сделать. У меня еще нет 50 баллов ... Я немного изменяю ваше решение и дополняю код ...
package main
import("bufio""fmt""io""os")
func main(){
f, err := os.Open("archiveName")if err !=nil{
fmt.Println(err)
os.Exit(1)}
defer f.Close()
r := bufio.NewReader(f)
line, err := r.ReadString(10)// line defined once for err != io.EOF {
fmt.Print(line)// or any stuff
line, err = r.ReadString(10)// line was defined before}}
Я не уверен, почему мне нужно снова проверять 'err', но в любом случае мы можем это сделать. Но главный вопрос заключается в том, почему Go не выдает ошибку с строкой предложения =>, err: = r.ReadString (10) внутри цикла? Он определяется снова и снова каждый раз, когда выполняется цикл. Я избегаю этой ситуации с моим изменением, любой комментарий? Я установил условие EOF в 'for' так же, как и в то время как. Спасибо
Другой способ заключается в использовании io/ioutilи stringsбиблиотек для чтения байт всего файла целиком, в преобразовать их в строку и разделить их с помощью « \n» (перевода строки) символа в качестве разделителя, например:
Технически вы не читаете файл построчно, однако вы можете анализировать каждую строку, используя эту технику. Этот метод применим к файлам меньшего размера. Если вы пытаетесь проанализировать массивный файл, используйте один из методов, который читает построчно.
Ответы:
ПРИМЕЧАНИЕ . Принятый ответ был правильным в ранних версиях Go. Смотрите ответ с наибольшим количеством голосов содержит более свежий идиоматический способ достижения этого.
В пакете есть функция ReadLine
bufio
.Обратите внимание, что если строка не помещается в буфер чтения, функция вернет неполную строку. Если вы хотите всегда читать всю строку в вашей программе одним вызовом функции, вам нужно будет инкапсулировать
ReadLine
функцию в вашу собственную функцию, которая вызываетReadLine
цикл for.bufio.ReadString('\n')
не полностью эквивалентен,ReadLine
потому чтоReadString
не может обработать случай, когда последняя строка файла не заканчивается символом новой строки.источник
В Go 1.1 и новее самый простой способ сделать это с помощью
bufio.Scanner
. Вот простой пример, который читает строки из файла:Это самый чистый способ читать
Reader
построчно.Есть одно предупреждение: сканер плохо справляется со строками длиннее 65536 символов. Если это проблема для вас, тогда вам, вероятно, стоит свернуть свою собственную
Reader.Read()
.источник
file, _ := os.Open("/path/to/file.csv")
а затем просканировал дескриптор файла:scanner := bufio.NewScanner(file)
defer file.Close()
.bufio.ErrTooLong
ошибку,bufio.Scanner: token too long
если строка слишком длинная. В этом случае вам придется использовать bufio.ReaderLine () или ReadString ().Использование:
reader.ReadString('\n')
\n
в конце возвращаемой строки.reader.ReadLine()
Я протестировал различные решения, предложенные при написании программы для тестирования сценариев, которые определены как проблемы в других ответах:
Я нашел это:
Scanner
Решение не обрабатывать длинные строки.ReadLine
Решение является сложным для реализации.ReadString
Решение является наиболее простым и работает для длинных линий.Вот код, который демонстрирует каждое решение, его можно запустить через
go run main.go
:Я тестировал на:
Результаты тестовой программы:
источник
defer file.Close()
Должно быть после проверки ошибок; в противном случае при ошибке он будет паниковать.РЕДАКТИРОВАТЬ: Начиная с go1.1, идиоматическое решение заключается в использовании bufio.Scanner
Я написал способ легко читать каждую строку из файла. Функция Readln (* bufio.Reader) возвращает строку (sans \ n) из базовой структуры bufio.Reader.
Вы можете использовать Readln для чтения каждой строки из файла. Следующий код читает каждую строку в файле и выводит каждую строку в стандартный вывод.
Ура!
источник
Существует два распространенных способа чтения файла построчно.
В моем тестовом примере ~ 250 МБ, ~ 2 500 000 строк , bufio.Scanner (использованное время: 0,395491384s) быстрее, чем bufio.Reader.ReadString (time_used: 0.446867622s).
Исходный код: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line
Считайте файл использования bufio.Scanner,
Чтение файла с использованием bufio.Reader,
источник
bufio.Reader
виду, что этот пример не будет читать последнюю строку в файле, если он не заканчивается новой строкой.ReadString
вернет как последнюю строку, так иio.EOF
в этом случае.Пример из этой сущности
но это дает ошибку, когда есть строка, которая больше, чем буфер сканера.
Когда это произошло, я использую
reader := bufio.NewReader(inFile)
create и concat моего собственного буфера, используяch, err := reader.ReadByte()
илиlen, err := reader.Read(myBuffer)
Другой способ, которым я пользуюсь (замените os.Stdin на файл, как указано выше), этот метод работает, когда строки длинные (isPrefix) и игнорирует пустые строки:
источник
-1
?Вы также можете использовать ReadString с \ n в качестве разделителя:
источник
bufio.Reader.ReadLine () работает хорошо. Но если вы хотите прочитать каждую строку по строке, попробуйте использовать ReadString ('\ n') . Не нужно изобретать велосипед.
источник
источник
В приведенном ниже коде я читаю интересы из CLI, пока пользователь не нажмет ввод, и я использую Readline:
источник
Мне нравится решение Lzap, я новичок в Go, я хотел бы попросить lzap, но я не смог этого сделать. У меня еще нет 50 баллов ... Я немного изменяю ваше решение и дополняю код ...
Я не уверен, почему мне нужно снова проверять 'err', но в любом случае мы можем это сделать. Но главный вопрос заключается в том, почему Go не выдает ошибку с строкой предложения =>, err: = r.ReadString (10) внутри цикла? Он определяется снова и снова каждый раз, когда выполняется цикл. Я избегаю этой ситуации с моим изменением, любой комментарий? Я установил условие EOF в 'for' так же, как и в то время как. Спасибо
источник
Вот пример с функцией, на которую
ReadFromStdin()
она похожа,fmt.Scan(&name)
но она принимает все строки с пробелами, например: «Hello My Name Is ...»источник
Другой способ заключается в использовании
io/ioutil
иstrings
библиотек для чтения байт всего файла целиком, в преобразовать их в строку и разделить их с помощью «\n
» (перевода строки) символа в качестве разделителя, например:Технически вы не читаете файл построчно, однако вы можете анализировать каждую строку, используя эту технику. Этот метод применим к файлам меньшего размера. Если вы пытаетесь проанализировать массивный файл, используйте один из методов, который читает построчно.
источник