Lendo arquivo linha por linha em Go: do iniciante ao profissional em 2025
\n\nVocê já se perguntou como ler eficientemente arquivos de log enormes ou processar dados CSV em Go? Se sim, você veio ao lugar certo. O tema da leitura de arquivos linha por linha é um dos mais populares no Stack Overflow, e não é por acaso. Go nos dá ferramentas poderosas, mas escolher a abordagem certa às vezes não é fácil.
\n\nNeste artigo, não vamos apenas mostrar como ler linhas de um arquivo. Vamos analisar as melhores práticas de 2025: desde o rápido bufio.Scanner até soluções bufferizadas personalizadas. Você aprenderá como evitar armadilhas (por exemplo, linhas com mais de 64KB), tratar erros corretamente e acelerar o trabalho com grandes volumes de dados.
Pronto? Vamos lá!
\n\nPor que ler linhas não é simplesmente "pegar e ler"?
\n\nEm Go, existem várias maneiras de ler arquivos, e cada uma é para uma tarefa específica. Alguns usam ioutil.ReadFile (aliás, desde Go 1.16 ele está deprecated), outros usam os.File com buffer. Mas para processamento linha por linha, o rei é bufio.Scanner.
Por quê? Porque ele:
\n- \n
- É eficiente em memória — não carrega o arquivo inteiro na RAM \n
- É simples de usar — mínimo de código \n
- Sabe tratar erros — verifica cada etapa \n
Mas há um detalhe: o buffer padrão do Scanner é de 64 KB. Se sua linha for maior — você receberá um erro. Para esses casos, são necessárias outras abordagens.
\n\nExemplo básico: bufio.Scanner
\n\nVamos começar com a maneira mais comum. Aqui está o código funcional mínimo:
\n\npackage main\n\nimport (\n "bufio"\n "fmt"\n "log"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("data.txt")\n if err != nil {\n log.Fatal(err)\n }\n defer file.Close()\n\n scanner := bufio.NewScanner(file)\n for scanner.Scan() {\n line := scanner.Text()\n fmt.Println(line)\n }\n\n if err := scanner.Err(); err != nil {\n log.Fatal(err)\n }\n}\n\n\nO que é importante aqui?
\n- \n
defer file.Close()— fecha o arquivo mesmo em caso de pânico \n scanner.Text()— retorna a linha sem o caractere de nova linha (\\n) \n scanner.Err()— verifica se houve erros durante a leitura \n
Este código é ideal para 90% das tarefas: configurações, logs, CSV pequenos.
\n\nProblema de linhas longas: aumentando o buffer
\n\nImagine: você está lendo um arquivo com linhas JSON de 100 KB. O Scanner falhará com o erro bufio.Scanner: token too long. A solução é aumentar o buffer:
scanner := bufio.NewScanner(file)\nbuf := make([]byte, 0, 1024*1024) // 1 MB\nscanner.Buffer(buf, 10*1024*1024) // máximo de 10 MB por linha\n\nfor scanner.Scan() {\n // ... processamento\n}\n\n\nImportante: o segundo parâmetro é o tamanho máximo da linha. Se a linha for maior — erro novamente. Escolha com margem, mas não gigantesca — a memória não é elástica.
\n\nQuando o Scanner não é adequado: alternativas
\n\nApesar da popularidade, bufio.Scanner não é universal. Aqui estão dois casos em que vale a pena considerar outra abordagem:
1. Arquivos enormes (gigabytes)
\nSe o arquivo for maior que a memória disponível (raro, mas acontece), o Scanner ainda é bom. Mas se você precisa ler dados binários ou processar linhas com delimitadores personalizados — é melhor usar bufio.Reader:
reader := bufio.NewReader(file)\nfor {\n line, err := reader.ReadString('\\n')\n if err != nil {\n break\n }\n // line contém \\n no final\n line = strings.TrimRight(line, "\\n")\n fmt.Println(line)\n}\n\n\nVantagem: você controla cada operação de leitura. Desvantagem: mais código e maior risco de erros.
\n\n2. Processamento sem carregar na memória
\nÀs vezes, você só precisa contar linhas ou encontrar um padrão. Para isso, bufio.Scanner é adequado