Help us understand the problem. What is going on with this article?

Ruby ファイルを読み込む(Dir.globなど)順番について

More than 1 year has passed since last update.

ディレクトリ内のファイルを読み込む順番、気にしたことありますか?
Rubyのファイル読み込み順についての考察です。
環境:ruby 2.3.1p112
macOS 10.13.6 (17G65)

はじめに

Rubyでディレクトリ内のファイルを処理する際、Dir.globを使います。
カレントのinディレクトリに
[txtファイル20個]

[その他のうんこファイル1万個]
があるとします。

txtファイルだけを処理したい場合はこうです。

Ruby
Dir.glob('./in/*.txt').each do |fn|
  puts fn
end

で結果はこうです。

Result
./in/180726_152833_118036_016.txt
./in/180726_152833_118673_017.txt
./in/180726_152833_111733_006.txt
./in/180726_152833_116294_013.txt
./in/180726_152833_119965_020.txt
./in/180726_152833_113602_009.txt
./in/180726_152833_109057_002.txt
./in/180726_152833_116926_014.txt
./in/180726_152833_119551_019.txt
./in/180726_152833_114285_010.txt
./in/180726_152833_109710_003.txt
./in/180726_152833_115679_012.txt
./in/180726_152833_112946_008.txt
./in/180726_152833_111092_005.txt
./in/180726_152833_115039_011.txt
./in/180726_152833_107647_001.txt
./in/180726_152833_112327_007.txt
./in/180726_152833_119093_018.txt
./in/180726_152833_117478_015.txt
./in/180726_152833_110460_004.txt

勘の鋭い方はお気づきかもしれませんが、
./inディレクトリにあるtxtファイルのファイル名は
「日付_時刻_マイクロ秒_番号.txt」
すなわち
「ファイルが生成された時刻がファイル名」
になっています。

ということは、、、、

ファイルが生成された順番に処理されてないじゃん!

順番に処理しなきゃなんない瞬間(とき)ってあるんだよ。
お父さんびっくりだよ…。

何はともあれgoogle

glob で取得したファイル一覧の順番は保証されるのか
ここによると
Ruby(2.4.2)順番は保証されるかは…不明…。

Does Dir.glob guarantee the order?
海外の方も気にしておられるようですが、OSによるよ!とか。悲しい…。

あんた、あの娘の何なのさ

じゃあDir.globの順番ってなんなのさ!この泥棒猫!ハアハア。
といいたいところですが、グッとこらえてgoogle。
フォルダー名の読み込み順番について

godanswer
ls -f で実際に取得した順番で表示されます。

との回答がありました。

ではでは

sh
ls -f ./in
.                             180726_152833_114285_010.txt
..                            180726_152833_109710_003.txt
180726_152833_118036_016.txt  180726_152833_115679_012.txt
180726_152833_118673_017.txt  180726_152833_112946_008.txt
180726_152833_111733_006.txt  180726_152833_111092_005.txt
180726_152833_116294_013.txt  180726_152833_115039_011.txt
180726_152833_119965_020.txt  180726_152833_107647_001.txt
180726_152833_113602_009.txt  180726_152833_112327_007.txt
180726_152833_109057_002.txt  180726_152833_119093_018.txt
180726_152833_116926_014.txt  180726_152833_117478_015.txt
180726_152833_119551_019.txt  180726_152833_110460_004.txt

ほ、ほんまや!

これは何の順番?

先程のTeratailの回答には「物理エントリ」順との回答もあります。
ページテーブル
ストレージに記憶(ページング)されている順ということかな…。
計算機的にはその方がいいのかもしれんが、人間にとっては困るんだyo!

じゃあDir:glob使えねーじゃん

初心に帰り、ls -aを使ってみる

ということで一旦Dir.globから脱退してみましょう。
globeから脱退したYOSHIKIのように。

Ruby
  files = `ls -a ./in`.split("\n")
  files.each do |f|
    puts f if /.*\.txt$/ =~ f
  end

Dir.globがls -fで取得したファイル順だっつーのなら、逆転の発想。
バッククォート記法でls -aして取得したファイルをsplitして配列化してeachします。

Result
180726_152833_107647_001.txt
180726_152833_109057_002.txt
180726_152833_109710_003.txt
180726_152833_110460_004.txt
180726_152833_111092_005.txt
180726_152833_111733_006.txt
180726_152833_112327_007.txt
180726_152833_112946_008.txt
180726_152833_113602_009.txt
180726_152833_114285_010.txt
180726_152833_115039_011.txt
180726_152833_115679_012.txt
180726_152833_116294_013.txt
180726_152833_116926_014.txt
180726_152833_117478_015.txt
180726_152833_118036_016.txt
180726_152833_118673_017.txt
180726_152833_119093_018.txt
180726_152833_119551_019.txt
180726_152833_119965_020.txt

おおっ!ファイル生成順にテキストが処理されているっぽい????
くれないだー!

はて?このままでいいのか?

ここで1つの疑念が生じます。

ファイル名順に処理しているのではないのか?と。

というわけで、ファイル名の先頭にランダムで3文字のアルファベットを付加してみましょう。
んで、さっきと同じ

Ruby
  files = `ls -a ./in`.split("\n")
  files.each do |f|
    puts f if /.*\.txt$/ =~ f
  end

(さっきとはファイル生成時刻は違うのでファイル名も違いますよ)

Result
AFC_180726_160348_137270_016.txt
BBU_180726_160348_133355_006.txt
CRV_180726_160348_135485_012.txt
DEU_180726_160348_132165_003.txt
FEX_180726_160348_132962_005.txt
FKQ_180726_160348_132576_004.txt
FWJ_180726_160348_138088_018.txt
IRY_180726_160348_134186_008.txt
KMK_180726_160348_134531_009.txt
KQG_180726_160348_136848_015.txt
LCY_180726_160348_133776_007.txt
OXJ_180726_160348_135874_013.txt
RHE_180726_160348_136242_014.txt
SWQ_180726_160348_135171_011.txt
TMT_180726_160348_138645_019.txt
VUA_180726_160348_137680_017.txt
VVM_180726_160348_134863_010.txt
WCV_180726_160348_139066_020.txt
ZSH_180726_160348_131737_002.txt
ZYD_180726_160348_131238_001.txt

案の定かよ!

ls -aはファイル名順に読み込むようです。

ls -atrではどうよ!

↓ここを参考
あるディレクトリ下の全ファイルを更新日時の古い順に詳細表示
lsには色んなオプションがあるんだけど、
-atrは「t:更新順」「r:昇順」に並べ替えします。

Ruby
  files = `ls -atr ./in`.split("\n")
  files.each do |f|
    puts f if /.*\.txt$/ =~ f
  end

これでどうだ!

Result
ZYD_180726_160348_131238_001.txt
ZSH_180726_160348_131737_002.txt
DEU_180726_160348_132165_003.txt
FKQ_180726_160348_132576_004.txt
FEX_180726_160348_132962_005.txt
BBU_180726_160348_133355_006.txt
LCY_180726_160348_133776_007.txt
IRY_180726_160348_134186_008.txt
KMK_180726_160348_134531_009.txt
VVM_180726_160348_134863_010.txt
SWQ_180726_160348_135171_011.txt
CRV_180726_160348_135485_012.txt
OXJ_180726_160348_135874_013.txt
RHE_180726_160348_136242_014.txt
KQG_180726_160348_136848_015.txt
AFC_180726_160348_137270_016.txt
VUA_180726_160348_137680_017.txt
FWJ_180726_160348_138088_018.txt
TMT_180726_160348_138645_019.txt
WCV_180726_160348_139066_020.txt

おおやったね!

#結論
Dir.globはファイル更新順には読み込まないので、バッククォート記法でls -atrでやる
(Dir.globにオプションがあればいいのに...)

投稿後にコメントを受けた結論

ここまでやってアレですが、投稿後、knuさんにコメントをいただきました。

godanswer
名前の辞書順: Dir.glob(pattern).sort
生成順: Dir.glob(pattern).sort_by { |fn| File.birthtime(fn) }
更新順: Dir.glob(pattern).sort_by { |fn| File.mtime(fn) }

のように自分でソートする、が答えです。

え!俺アホやん...。

Ruby
  files = Dir.glob('./in/*.txt').sort_by { |fn| File.birthtime(fn) }
  files.each {|fn| puts fn}
Result
ZYD_180726_160348_131238_001.txt
ZSH_180726_160348_131737_002.txt
DEU_180726_160348_132165_003.txt
FKQ_180726_160348_132576_004.txt
FEX_180726_160348_132962_005.txt
BBU_180726_160348_133355_006.txt
LCY_180726_160348_133776_007.txt
IRY_180726_160348_134186_008.txt
KMK_180726_160348_134531_009.txt
VVM_180726_160348_134863_010.txt
SWQ_180726_160348_135171_011.txt
CRV_180726_160348_135485_012.txt
OXJ_180726_160348_135874_013.txt
RHE_180726_160348_136242_014.txt
KQG_180726_160348_136848_015.txt
AFC_180726_160348_137270_016.txt
VUA_180726_160348_137680_017.txt
FWJ_180726_160348_138088_018.txt
TMT_180726_160348_138645_019.txt
WCV_180726_160348_139066_020.txt

なるほど〜。これでスッキリ!

devzooiiooz
Ruby on Railsの開発が主です。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away