ファイルの中身を1行ずつテキスト処理するには

Go言語 Java Scala PHP Python Ruby Perl sh

Go言語 (golang)

Scanner を使う例

import "bufio"
import "fmt"
import "os"

func main() {
    var fp *os.File
    var err error
    if len(os.Args) < 2 {
        fp = os.Stdin
    } else {
        fp, err = os.Open(os.Args[1])
        if err != nil {
            panic(err)
        }
        defer fp.Close()
    }

    scanner := bufio.NewScanner(fp)
    for scanner.Scan() {
        line := scanner.Text()
        // lineはstring型で最後の改行が含まれない
        fmt.Println(line)
    }

}

ReadString を使う例

package main

import "bufio"
import "fmt"
import "io"
import "os"

func main() {
    var fp *os.File
    var err error
    if len(os.Args) < 2 {
        fp = os.Stdin
    } else {
        fp, err = os.Open(os.Args[1])
        if err != nil {
            panic(err)
        }
        defer fp.Close()
    }

    reader := bufio.NewReader(fp)
    for {
        line, err := reader.ReadBytes('\n')
        if err != nil && err != io.EOF {
            panic(err)
        }

        // lineは[]byte型で最後の改行が含まれる
        os.Stdout.Write(line)

        if err == io.EOF {
            break
        }
    }
}

Java

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

BufferedReader reader = new BufferedReader(
    new InputStreamReader(new FileInputStream(filePath), "UTF-8"));
while (true) {
    String line = reader.readLine();
    if (line == null)
        break;
    // line には最後の改行を含まない
    ...;
}
fclose(reader);

BufferedReader#readLine()"\n", "\r", "\r\n" のいずれかを改行とみなす。

Scala

Scalaの標準のライブラリを使う例

import scala.io.Source;

val source = Source.fromFile(filePath);
source.getLines.foreach { line =>
  // line には最後の改行を含まない
  ...;
}
source.close();

ファイルではなく標準入力を行単位で処理するには

import scala.io.Source;

Source.fromInputStream(System.in).getLines.foreach { line =>
  // line には最後の改行を含まない
  ...;
}
source.close();

scala.collection.Iterator を自作する例

def stdinLineIterator(): Iterator[String] = {

  var fp = new java.io.BufferedReader(new java.io.InputStreamReader(System.in, "UTF-8"));
  var nextLine: String = null;

  new Iterator[String] {

    def hasNext: Boolean = {
      if (fp == null) {
        false;
      } else {
        if (nextLine == null) {
          nextLine = fp.readLine;
        }
        if (nextLine == null) {
          fp.close();
          false;
        } else {
          true;
        }
      }
    }

    def next(): String = {
      if (!hasNext) {
        throw new java.util.NoSuchElementException();
      }
      val ret = nextLine;
      nextLine = null;
      ret;
    }

  }

}

この stdinLineIterator が返す各行の文字列には最後の改行を含まない。

PHP

$fh = fopen($filePath, 'r');
if ($fh) {
  while (($line = fgets($fh)) !== false) {
    // ファイルの最後が改行の場合、その改行を含む行がループの最後になる。
    // $line には最後の改行を含む。
    
    // 改行を削除
    $line = preg_replace('/\r?\n?\z/', '', $line);
    
    ...;
  }
  fclose($fh);
}

fgets'\r'(0x0D) を改行としてみなさない。ただし auto_detect_line_endings というPHPの設定値をphp.iniファイルまたはini_set関数でonにした場合には 0x0D も改行とみなされるらしい。

標準入力に対しては

$fh = fopen('php://stdin', 'r');

としてファイルポインタを取得するか、もしくはfopenせずにファイルポインタを以下のように STDIN と書けばよい。

while (!feof(STDIN)) {
  $line = fgets(STDIN);
  // $line には最後の改行を含む
  ...;
}

fgets 関数 | PHP Manual
http://php.net/manual/ja/function.fgets.php

auto_detect_line_endings | PHP Manual
http://php.net/manual/ja/filesystem.configuration.php#ini.auto-detect-line-endings

Python

for line in open(filePath):
    # line には最後の改行を含む
    line = line[:-1] # 最後の文字(改行かもしれない改行じゃないかもしれない)を取る
    # line = line.rstrip() とすれば末尾の改行を含めた空白を削除する

Python2では以下のように xreadlines メソッドを書いても同じ。

for line in open(filePath, 'r').xreadlines():

以下のように readlines メソッドを書いてもループはできるが、この場合はループ開始時にいったんファイルの中身をすべて読み込んでしまうので、大きなファイルを扱うときにメモリを大量に消費する。

for line in open(filePath, 'r').readlines():

メソッドを書かない最初の例ではopenメソッドが返すファイルオブジェクトがイテレータ型であることを利用している。イテレータ型であるのでリストの内包表記に使うこともできる。

lines = [line for line in open(filePath)]

標準入力から1行ずつ読み込む例

import sys

for line in sys.stdin:
    # line には最後の改行を含む
    ...

Ruby

IO.foreach(filePath) do |line|
  ...
end

または

open(filePath) do |fh|
  fh.each do |line|
    # line には最後の改行を含む
    ...
  end
end

または

open(filePath) do |fh|
  while line = fh.gets
    # line には最後の改行を含む
    ...
  end
end

標準入力から1行ずつ読み込む例

while line = STDIN.gets
  # line には最後の改行を含む
  ...
end

Perl

open(IN, '<', $filePath) or die "Cannot open";
while (my $line = <IN>) {
    # $line には最後の改行を含む
    ...;
}
close IN;

または

open(IN, '<', $filePath) or die "Cannot open";
my @lines = <IN>;
# @lines には配列でファイル全体が保存されるので、あとで利用する
close IN;

while文は修飾子の書き方もできるので、単純な処理であれば以下のような書き方も可能。

open(IN, '<', './hoge.txt') or die "Cannot open";
my $line;
print $line while $line = <IN>;
close IN;

またはさらに短縮して、

open(IN, '<', './hoge.txt') or die "Cannot open";
print $_ while <IN>;
close IN;

または

open(IN, '<', './hoge.txt') or die "Cannot open";
print while <IN>;
close IN;

openせずに <IN> の代わりに <STDIN> と書けば、標準入力を1行ずつ処理できる。

sh (シェルスクリプト)

シェルスクリプトでファイルから1行ずつ読み込んで、繰り返し処理させるには read というシェルの組み込みコマンドを使うとよい。

read は標準入力から1行読み込んで変数に保存することができる。read 変数名 というふうに使う。

cat ./hoge.txt | while read line; do
    # $line で各行の内容にアクセスできる
    ...
done

catの出力をパイプでwhile文につなぐことで、 while文の中にあるreadコマンドは ./hoge.txt の内容を1行ずつ読み込める。

readのあとにlineと指定しているので、読み込んだ内容は $line という変数に保存される。 while文は、入力の行数だけ繰り返す。途中に空行があっても問題ない。

ただし、この方法だとwhile文がサブシェルの中で実行されるかもしれないので、 whileループの中で変数に代入したり、exitしようとしたりしてもうまくいかないかもしれない。ループの中の変数代入が、ループの外に反映されない、ということが起こる。

また、while文の中で標準入力を受け取るコマンドがあると、cat ./hoge.txt からの入力を消費してしまって、 read line での読み込みが最初の1行しかできなくなってしまう。

sshコマンドでリモートホストでコマンドを実行させるときに、標準入力を消費しないつもりだったのに無意味に消費してしまっていることがよくある。そんなときは ssh -n ... のように -n をつける。

ループの中身をまるごと標準入力から隔離したい場合は、自分はよく以下のようにする。

cat ./hoge.txt | while read line; do (
    # $line で各行の内容にアクセスできる
    ...
) </dev/null; done
このサイトは筆者(hydrocul)の個人メモの集合です。すべてのページは永遠に未完成です。
スポンサーリンク