質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Q&A

解決済

2回答

717閲覧

perlのファイルハンドルでうまく削除できない

yooashleaf

総合スコア32

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

0グッド

0クリップ

投稿2019/03/14 13:41

perlでファイルハンドルの使い方がイマイチ理解出来ていないのですが、
以下のようにdata.txtに入っているもので、
行を削除したい時に、残留物が残るのですが、どうすればうまくいくでしょうか?
正しい使い方を教えて頂けると助かります。
宜しくお願い致します。

▼data.txtの内容
A組<>井上<>男<>172cm<>
B組<>山田<>女<>150cm<>
A組<>鈴木<>男<>180cm<>
C組<>佐藤<>男<>169cm<>

▼特定の行を消す(C組の人を消す)

open(FH,"+< data.txt"); flock(FH, 2); while (<FH>) { @log = split(/<>/,$_); if($log[0] eq "C組"){ $data .= ""; }else{ $data .= "$_"; } } seek(FH, 0, 0); print FH "$data"; close(FH);

▼実行後のdata.txtの内容
A組<>井上<>男<>172cm<>
B組<>山田<>女<>150cm<>
A組<>鈴木<>男<>180cm<>
C組<>佐藤<>男<>169cm<>
と消えていなかったり、C組が途中の行にあると
最後の行に
<>男<>180cm<>
など中途半端なデータが残ります。

+<
はファイル内容をクリアしないので、行が残ってしまうと思うので、
本当はseekの前にファイルをクリアするものがあればうまくいくのかなと思いますが、
そもそも根本的に考え方が間違っている気がします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

Perlに限らないですが、

  • ファイルの最初にseek()してから書き込むと、ファイルの元の内容に上書きします。
  • 書き込んだ長さが元の内容と同じかより長ければいいですが、より短ければ、元の内容のうち上書きされなかった部分は残ります。

元の内容が残らないようにしたければ、おっしゃるとおりtruncate()を使って書き込んだ長さと同じ長さでファイルを切り詰めるなどする必要があります。

書き込んだ長さを計算するのが面倒なら、書き込む前に長さゼロに切り詰めちゃってもいいですね。「書き込んだ長さが元の内容と同じかより長ければいい」んですから。ただ、切り詰めた内容は読めなくなるので、全部読んでから切り詰める必要があります。

perl

1open my $fh,+<, $filename; 2# 全部読んで$dataを用意しておく 3seek $fh, 0, 0; 4truncate $fh, 0; 5print $fh $data; 6close $fh;

追記。別回答でファイルロックが必要とのことですが、flock()が使えるのなら、本回答ではファイルハンドルを一回しか開かないのでそれでできます。

投稿2019/03/14 14:45

編集2019/03/14 14:57
ikedas

総合スコア4227

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yooashleaf

2019/03/14 15:00

truncateで全て解決しそうです! ありがとうございました!
ikedas

2019/03/14 15:15

あと、flock()は「アドバイザリロック」なので、すでにほかのプロセスがロックしていてflock()が失敗しても書き込みはできてしまいます。失敗したら書き込みしないようにする必要があります。
ikedas

2019/03/14 23:32

あ、そうか。LOCK_NBは指定してないのでブロックしますね。先のコメントは余計なことを書きました。
yooashleaf

2019/03/20 14:33

アドバイザリロックについて調べることができましたので、後学のためになりました!本当に感謝致します!
guest

0

まず、話がややこしくなりすぎるのでreadとwriteは完全にセパレートしましょう。
次に、この書き方だと日本語文字列の比較や加工で問題が出てくる可能性が高いので、きちんと内部コード化するように書きましょう。
以下はイメージです。

#!/usr/bin/perl use strict; use warnings; use utf8; # スクリプト内のリテラルを内部コードとして扱う。 my $data = ''; open(my $fhi, "<:utf8", "jpn.csv") or die; # readするデータは内部コードにデコードされる。 while(<$fhi>){ my @f = split(/<>/, $_); if($f[0] ne 'C組'){ $data .= $_; } } close $fhi; # 上書き前にきちんとクローズ。 open(my $fho, ">:utf8", "jpn.csv") or die; # writeデータは全てutf8にエンコードされる。 print {$fho} $data; close $fho;

なお、この質問の範囲ならワンライナーでいけますね。

perl -i.bak -MEncode -Mutf8 -F'<>' -alne '(decode("utf8", $F[0]) ne "C組") and print' jpn.csv

投稿2019/03/14 14:22

編集2019/03/14 14:30
KojiDoi

総合スコア13669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yooashleaf

2019/03/14 14:27

ご回答&指摘して頂きまして、ありがとうございます! 不特定多数の人が使うので、出来れば読み→書きの間でファイルをロックしたいのですが、 そのような方法はありますでしょうか?
KojiDoi

2019/03/14 14:33

書き込みの段階で排他処理をしてください。サンプルは検索すれば見つかると思います。
yooashleaf

2019/03/14 15:01

排他処理の助言ありがとうございます!検索して理解を深めます!
yooashleaf

2019/03/14 15:02

ワンライナーでの表記も有り難いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問