Skip to content

ファイルを1行ずつ読む snippet

テキストファイルをメモリに載せずに1行ずつ反復します。すべてのログスキャンとバッチフィードの基本形です。各エコシステムはストリーミングのプリミティブの隣に、丸ごと読む双子(readFileSync().split と readline、ReadAllLines と ReadLines、IO.readlines と foreach)を潜ませており、誤った方を選ぶと数GBのログがメモリ不足キルに変わります。端々での注意点として、多くのAPIは \n を取り除きますが CRLF ファイルの取り残された \r はそのままにしますし、末尾に改行がない最終行も1行として届きます。SQLは省略されています。エンジンが読めるのは自分のサーバーのディスクだけ(Postgres の pg_read_file はスーパーユーザー権限の下で)で、あなたのマシン上のファイルは決して読めません。それはクライアントドライバの仕事です。

テキストファイルをメモリに載せずに1行ずつ反復します。すべてのログスキャンとバッチフィードの基本形です。各エコシステムはストリーミングのプリミティブの隣に、丸ごと読む双子(readFileSync().split と readline、ReadAllLines と ReadLines、IO.readlines と foreach)を潜ませており、誤った方を選ぶと数GBのログがメモリ不足キルに変わります。端々での注意点として、多くのAPIは \n を取り除きますが CRLF ファイルの取り残された \r はそのままにしますし、末尾に改行がない最終行も1行として届きます。SQLは省略されています。エンジンが読めるのは自分のサーバーのディスクだけ(Postgres の pg_read_file はスーパーユーザー権限の下で)で、あなたのマシン上のファイルは決して読めません。それはクライアントドライバの仕事です。

Runnable recipe · 14 languages
Files & Datafilesiostreaming

Every language

14 languages, copy-ready. One at a time with syntax highlighting, or all inline.

JSJavaScript
import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

const rl = createInterface({
  input: createReadStream('app.log'),
  crlfDelay: Infinity,
});

for await (const line of rl) {
  console.log(line);
}

readline over a createReadStream never holds the whole file — readFileSync('app.log', 'utf8').split('\n') is the memory bomb it replaces. crlfDelay: Infinity folds \r\n into one break so Windows logs don't leave a stray \r.

TSTypeScript
import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

const rl = createInterface({
  input: createReadStream('errors.log'),
  crlfDelay: Infinity,
});

let errors = 0;
for await (const line of rl) {
  if (line.startsWith('ERROR')) errors++;
}
console.log(`${errors} error line(s)`);

Same runtime as the JavaScript version — line is inferred as string, so no nullish handling is needed. The async iterator ends cleanly at EOF, including a final line with no trailing newline.

GoGo
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	f, err := os.Open("app.log")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		panic(err)
	}
}

Scan() strips \n and \r\n and yields lines up to 64 KiB by default — a longer line returns bufio.ErrTooLong, so call scanner.Buffer(make([]byte, 0, 64*1024), 1<<20) for ragged logs. Always check scanner.Err() after the loop; the for condition alone swallows read errors.

RsRust
use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() -> std::io::Result<()> {
    let file = File::open("app.log")?;
    for line in BufReader::new(file).lines() {
        println!("{}", line?);
    }
    Ok(())
}

BufRead::lines strips \n and a trailing \r and yields io::Result per line — apply ? to each, since a mid-file read error surfaces exactly there. Without BufReader each line costs a syscall; File alone is unbuffered.

PHPPHP
<?php
$handle = fopen('app.log', 'r');
while (($line = fgets($handle)) !== false) {
    echo rtrim($line, "\r\n"), PHP_EOL;
}
fclose($handle);

fgets keeps the trailing newline and returns false at EOF — compare with !== false, because a last line of "0" loosely equals false and would end the loop a line early. file('app.log') loads every line into an array instead; rtrim removes the \n and the \r of CRLF.

PyPython
from pathlib import Path

with Path("app.log").open(encoding="utf-8") as f:
    for line in f:
        print(line.rstrip("\n"))

Iterating the file object streams through a buffered reader and speaks universal newlines — \n, \r\n, and a lone \r all arrive as \n (open with newline='' only for csv). read().split('\n') slurps and mishandles CRLF; the last line without a newline still arrives.

CC
#include <stdio.h>

int main(void) {
    FILE *f = fopen("app.log", "r");
    if (f == NULL) {
        perror("fopen");
        return 1;
    }

    char line[256];
    while (fgets(line, sizeof line, f) != NULL) {
        fputs(line, stdout); /* keeps the trailing '\n' */
    }
    fclose(f);
    return 0;
}

fgets stores at most sizeof line - 1 bytes and keeps the '\n' — a line longer than the buffer arrives split across several calls. It returns NULL at EOF and on error; ferror(f) tells them apart. For unbounded lines use POSIX getline(&buf, &cap, f), which allocates.

C++C++
#include <fstream>
#include <iostream>
#include <string>

int main() {
    std::ifstream in("app.log");
    std::string line;
    while (std::getline(in, line)) {
        std::cout << line << '\n';
    }
}

std::getline strips the '\n' delimiter but leaves a '\r' from CRLF files — trim before exact comparisons. ifstreams buffer by default, and the returned stream converts to false at EOF; std::istringstream over a slurped std::string is the non-streaming alternative.

C#C#
using System.IO;

foreach (string line in File.ReadLines("app.log"))
{
    Console.WriteLine(line);
}

File.ReadLines is the lazy IEnumerable<string> — File.ReadAllLines() loads every line into an array first and looks nearly identical at the call site. ReadLines decodes UTF-8, splits on \n and \r\n, and never yields a phantom empty line after a trailing newline.

JvJava
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class ReadLines {
    public static void main(String[] args) throws IOException {
        try (Stream<String> lines = Files.lines(Path.of("app.log"))) {
            lines.filter(line -> line.contains("ERROR"))
                 .forEach(System.out::println);
        }
    }
}

Files.lines streams lazily and decodes UTF-8 by default — Files.readAllLines slurps a List<String> first. The stream keeps the file open, so try-with-resources is not optional: skipping it leaks the descriptor until GC. Java 11+ for Path.of.

SwSwift
import Foundation

@main
struct ReadLines {
    static func main() async throws {
        let url = URL(fileURLWithPath: "app.log")
        for try await line in url.lines {
            print(line)
        }
    }
}

URL.lines (macOS 13 / iOS 16+) streams the file as an AsyncSequence of lines, and @main with an async main() makes the process await it — String(contentsOf:) + split is the slurping trap it replaces. On older runtimes, read Data chunks via FileHandle and split on \n yourself.

KtKotlin
import java.io.File

fun main() {
    File("app.log").useLines { lines ->
        lines.forEach(::println)
    }
}

useLines closes the handle when the block returns — the Sequence is lazy and single-shot, so consume it inside the block. readLines() is the array-slurping sibling; forEachLine { } is the inline shortcut over the same machinery.

RbRuby
File.foreach('app.log') { |line| puts line }

foreach yields one line (newline included) at a time in constant memory — IO.readlines('app.log') materializes the whole array first. Call line.chomp when the trailing "\n" would pollute a comparison or a hash key.

ZigZig
const std = @import("std");

pub fn main() !void {
    var file = try std.fs.cwd().openFile("app.log", .{});
    defer file.close();

    var buffered = std.io.bufferedReader(file.reader());
    const reader = buffered.reader();
    const stdout = std.io.getStdOut().writer();

    while (try reader.readUntilDelimiterOrEofAlloc(std.heap.page_allocator, '\n', 1024 * 1024)) |line| {
        defer std.heap.page_allocator.free(line);
        try stdout.print("{s}\n", .{std.mem.trimRight(u8, line, "\r")});
    }
}

Stdlib only, and File has no newline-aware helper: bufferedReader wraps the fd, then readUntilDelimiterOrEofAlloc allocates each line without the delimiter and returns null at EOF (ending the while). A '\r' before the '\n' is yours to trim, and the max-size argument turns a newline-free binary blob into error.StreamTooLong instead of an unbounded allocation.