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

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

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

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

sed

sedとは、POSIX環境のために作られたコマンドラインエディタです。sedは編集スクリプトの指示のもとに複数のファイルを編集し、標準出力にその結果を出力します。

Q&A

解決済

5回答

1983閲覧

perlでファイルへprintした際の最終行

ransky

総合スコア12

Perl

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

sed

sedとは、POSIX環境のために作られたコマンドラインエディタです。sedは編集スクリプトの指示のもとに複数のファイルを編集し、標準出力にその結果を出力します。

0グッド

2クリップ

投稿2019/04/24 00:50

編集2019/04/24 01:20

以下のようなコードでテキストファイルを出力させます。
自分が意図した処理はできているのですが、出力したoutput.txtを開くと、最終行に改行記号もない空の行(この表現が正しいのか不明ですが)があります。
下のコードの$Inputに使用しているENST_list.txtも別のperlコードで出力しているのですが、同じく最終行に空の行があり、手動でその行を消してからでないと下のコードがうまく動きません。

どのような方法で解決できるか、ご教授いただけると幸いです。

#やりたいこと
1)perlのファイル出力で、下の例のような3行目が出ないようにしたい、または
2) 何らかの方法で3行目を削除したい

1行目;A\n
2行目;B\n
3行目;何もない行

#試したこと
perlで空の行問題が解決できないので、出力した後に、
ファイルの最終行である空の行をsedで削除しようとしたのですが、sedで認識される最終行($)は、改行コードが存在している一番最後の行(実際にはその下に空の行がある)のようでした。
sedで消えるのは上の例だと2行目で、
1行目;A\n
2行目;何もない行
という結果になってしまいます。

#コード

perl

1my $outfile = 'output.txt'; 2my $Input = 'ENST_list.txt'; #最終行に空の行があると1行目のマッチングが成立しなくなる 3my $bed = 'hg19_Ensembl_gene.bed'; 4open(IN , $Input) or die "$!"; 5 6 while (my $In_line = <IN>) { 7 chomp($In_line); 8 @In_data = split(/\t/, $In_line); 9 10 open(DATA, "+>> $outfile") or die "$!"; 11 open(BN, $bed) or die "$!"; 12 13 while(my $bed_line = <BN>){ 14 15 chomp($bed_line); 16 @bed_data = split(/\t/, $bed_line); 17 18 if ("$In_data[0]" eq "$bed_data[3]" ){ 19 20 print DATA "@bed_data\n"; 21 } 22 } 23 close BN; 24 close DATA; 25 }

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

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

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

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

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

showkit

2019/04/24 01:03

コードは 書き方を参考に コード行として記述することをお勧めします。 プログラム以前に 1行目;A\n 2行目;B\n と書けば、当然 最後に空行が出力されますので 2行目: B と書くしかないですね。書いた後で末尾の 改行を消す手もあるかと思います。
ransky

2019/04/24 01:27

ご回答ありがとうございます。 コード行として記述しました、書き方がわかっていなくて申し訳ありませんでした。 末尾の改行を消すということは、chompでしょうか、考えてみます。
guest

回答5

0

perlのファイル入出力について詳しくないので間違っていたらすみませんが、

bash

1$ sed '$d'

では削除できないのですか?
つまり上記のコードの最後に下記の1行を足して実現できないのでしょうか?

perl

1`sed -i '$d' $outfile`;

投稿2019/04/24 06:53

amanoese

総合スコア132

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

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

0

決してスマートなやり方ではないし、とてもお勧めできませんが

perl

1#!/usr/local/bin/perl 2 3open(IN, "< in.txt"); 4my @read = <IN>; 5close(IN); 6 7pop(@read); 8 9open(OUT, "> out.txt"); 10print(OUT @read); 11close(OUT);

で、お望みの動きになります(自分の環境で動作確認)。
perl のシェバンやファイル名は適宜、置き換えてください。

投稿2019/04/24 06:01

showkit

総合スコア1638

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

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

KojiDoi

2019/04/24 06:05

どう考えても質問とは無関係です。
showkit

2019/04/24 06:11

自分でも申しておりますが、とてもスマートではないですが・・・。 「2) 何らかの方法で3行目を削除したい」のひとつの解 にはなっていませんでしょうか?
ssasaki

2019/04/24 07:15

これ、単純に3行目に何が入っていても削除されますよね。 質問内容からすると目的には合致していないと思います。
showkit

2019/04/24 07:21

ははぁ、そういう意味ですね。質問者さんの状況がわからないので。逼迫しているのかどうか、仮に逼迫しているのであれば、まず、現在の問題を解決したいということもありますので。 とりあえず、今の 現象のみを解決するという一つの方法として提示しておりました。 ご指摘ありがとうございます。
guest

0

「改行の存在しない空の行」というのがどうもよく分からないのですが、とりあえず対症療法を考えてみました。

外側のループ@In_data = split(/\t/, $In_line);の直後に(defined $In_data[1]) or next;
と加えて、「分割すべきデータがないならループの実行をスキップする」ようにすれば空行の出力はなされないと思います。

しかしその前に「別のperlコード」がゴミを吐き出すのを修正しておかないと、あとあと何か作業するたびに同じ問題に手間取ることになると思いますよ。

あと、なぜループの中で出力ファイルのopenとcloseを繰り返しているのでしょうか? まあ動くけど、無駄じゃないですか?

投稿2019/04/24 03:49

KojiDoi

総合スコア13671

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

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

ransky

2019/04/24 04:52

ご回答ありがとうございます。 openとcloseは位置を動かして書き直しても動きました。 「空の行」が出るのは出力ファイルの最終行のみで、 print DATA "@bed_data\n";としている以上は 1行目;A\n 2行目(文字のある最終行);B\n 3行目(最終行);何もない行 という形式で文字列出力されるのがprintの機能として正しいという理解でよいのでしょうか。
guest

0

ベストアンサー

既にたどり着いているようですが、<> で入力を拾う時には改行を含めて入ってくるので、改行を除去したいのであれば chomp で処理する必要があります。

1行目;A\n
2行目;B\n
3行目;何もない行

というのは正しい形式で、むしろ

1行目;A\n
2行目;B

の方が想定外のことが起きやすい印象はあります。

現在のコードには chomp が入っていますが、これでも動作していない状況なのでしょうか?

投稿2019/04/24 01:48

ssasaki

総合スコア1167

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

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

ransky

2019/04/24 02:30

ご回答ありがとうございます。 現在のコード自体で、やりたいことはできていますので、動作していると言って良いと思います('ENST_list.txt'が空行を含まない限り)。 しかし、 'ENST_list.txt'(別のperlスクリプトでprintしたもの)の最終行に空行ある場合には、 $Inputの1行目の値で、明らかに ("$In_data[0]" eq "$bed_data[3]" )が成立しているのに、print DATA "@bed_data\n"されないという結果になりました。 原因が理解できない中で'ENST_list.txt'にある最終行の空行を削除した場合に、$Inputの1行目の値で ("$In_data[0]" eq "$bed_data[3]" )が成立するようになり、print DATA "@bed_data\n"が実行されました('ENST_list.txt'にある最終行の空行を削除した以外にファイルに変更は加えていません)。 とにかく動けばよいという考えで出力時に 1行目;A\n 2行目;B という形式にできればよいと考えて質問したのですが、それよりも、 1行目;A\n 2行目;B\n 3行目;何もない行 という形式で動くように上記コード側を改変したほうが正しい気がしてきました。
ssasaki

2019/04/24 02:48

「明らかに ("$In_data[0]" eq "$bed_data[3]" )が成立している」はどのように検証しましたか? 可能性としては、ファイルによって改行コードが異なっているため、上記の条件が成立していないという可能性もあります。 ※結構ありがちな話です。 「最終行の空行を削除」は具体的にどのようにして削除したのでしょうか? エディタで編集ですか?それとも何らかのコマンドでしょうか?
ransky

2019/04/24 03:26

成立しているという検証のために、上記のコードに、”>”を左端においた3行を書き加えました。 @bed_data = split(/\t/, $bed_line); > print "In_data[0]>$In_data[0]\n"; > print "bed_data[3]>$bed_data[3]\n"; if ("$In_data[0]" eq "$bed_data[3]" ){ print "@bed_data\n"; > print DATA "@bed_data\n"; そうすると、標準出力に In_data[0]>ENST00000469484  #これは$Inputの1行目の値です bed_data[3]>ENST00000469484 #これは$bedの2行目の値です と出力されるので、("$In_data[0]" eq "$bed_data[3]" )が見た目には成立しているのではないかと解釈しました。 しかし、この場合には、 print "@bed_data\n"; print DATA "@bed_data\n"; は実行されません。 「最終行の空行を削除」として、エディタで、'ENST_list.txt'にある最終行の空行を削除したのちに同じコマンドを実行した場合には、標準出力上で、 In_data[0]>ENST00000469484 bed_data[3]>ENST00000469484 print "@bed_data\n"が実行(長いので省略) となり、print DATA "@bed_data\n"が実行されてファイルに出力されます。ファイルは全てLFになっています。
ransky

2019/04/24 03:28

ちなみに$bedの'hg19_Ensembl_gene.bed'は最終行に空行を含んでいます。
ransky

2019/04/24 03:46

さらに、同じコードを以下のようなテストファイルに対して実行すると、 Input_test.txtの最後に空行があっても期待通りに動き、 試しにInput_test.txtの最後の空行をエディタで削除しても同じように動き、 挙動が理解できない状態です。 (output_test.txtがタブ区切りで出力されると思ったのですが、それはできていません。 spaceからtabへの変換は簡単にできるので、できなくても今の所問題はないのですが)。 ------------------------ Input_test.txt 1行目; A[tab]B[tab]C[\n] 2行目; D[tab]E[tab]F[\n] 3行目; G[tab]H[tab]I[\n] 4行目; 空行 ------------------------ Bed_test.txt 1行目; 1[tab]2[tab]3[tab]D[\n] 2行目; 4[tab]5[tab]6[tab]A[\n] 3行目; 7[tab]8[tab]9[tab]G[\n] 4行目; 空行 標準出力 ------------------------ IN_data[0]>A bed_data[3]>D IN_data[0]>A bed_data[3]>A 5 6 7 A IN_data[0]>A bed_data[3]>G IN_data[0]>D bed_data[3]>D 1 2 3 D IN_data[0]>D bed_data[3]>A IN_data[0]>D bed_data[3]>G IN_data[0]>G bed_data[3]>D IN_data[0]>G bed_data[3]>A IN_data[0]>G bed_data[3]>G 9 10 11 G ------------------------ output_test.txt 1行目; 4[space]5[space]6[space]A[\n] 2行目; 1[space]2[space]3[space]D\n] 3行目; 7[space]8[space]9[space]G[\n] 4行目; 空行
KojiDoi

2019/04/24 06:07

> そうすると、標準出力に In_data[0]>ENST00000469484  #これは$Inputの1行目の値です bed_data[3]>ENST00000469484 #これは$bedの2行目の値です > In_data[0]>ENST00000469484  #これは$Inputの1行目の値です bed_data[3]>ENST00000469484 #これは$bedの2行目の値です と出力されるので 問題が発生するのが対象が空の行であるときであるという説明と矛盾しているように見えますが?
KojiDoi

2019/04/24 06:17

そもそも、改行を含まない空の行というのがおかしいので(それは「存在しない」と同義)実際には想定外のprintableではない文字が何か入っていてそれが期待と違う挙動を引き起こしているのではないでしょうか。
ransky

2019/04/24 06:51

「改行を含まない空の行」という表現がおかしいのですね。 複数の行が存在するファイルで、文字列を含む最終行の末尾に改行記号がある場合、 エディタ(Visual Studio Code)で表示すると 1行目;A\n 2行目;B\n 3行目;何もない行 という風に見えるので、3行目を「改行を含まない空の行」と表現していました。2行目の改行コードを削除すると、 1行目;A\n 2行目;B とエディタ上で表示されます。 問題の発生条件を再度示しますと、 $Inputファイルの最終行末尾に改行記号がある場合、 $Inputの1行目のデータ;In_data[0] と $bedの2行目のデータ;bed_data[3] が同じなのに、同じと認識されない。 $Inputの1行目のデータ(In_data[0]を含めて)は何も変更せず、$inputの最終行の改行コードのみを削除した場合には、 $Inputの1行目のデータ;In_data[0] と $bedの2行目のデータ;bed_data[3] が同じと認識されて、ifの中身が実行されてファイル出力される。ということです。 この説明で通じるでしょうか。
ssasaki

2019/04/24 07:06

提示された情報だけを見ると、やはりファイルの改行コードが異なるように思えますね。 「ファイルは全てLFになっています。 」と書かれていますが、これはどのようにして調べましたか? 例えば、エディタや設定によっては自動的に改行コードを変えて開くようなものもあると思いますので、エディタでの改行コードは不正確である可能性があります。 Linux であれば例えば、od -c ENST_list.txt といった感じで改行コードがどうなているか調べることができます。 それはそれとして、以下のように読み込み行を s/\r\n/\n/; した場合にはどうなりますか? 正常に動くようにならないですか? while (my $In_line = <IN>) { $In_line =~ s/\r\n/\n/; chomp($In_line); @In_data = split(/\t/, $In_line); open(DATA, "+>> $outfile") or die "$!"; open(BN, $bed) or die "$!"; while(my $bed_line = <BN>){ $bed_line =~ s/\r\n/\n/; chomp($bed_line); @bed_data = split(/\t/, $bed_line); if ("$In_data[0]" eq "$bed_data[3]" ){ print DATA "@bed_data\n"; } } close BN; close DATA; }
KojiDoi

2019/04/24 07:06

最後のifブロックを次のように書き換えて見てください。想定外の現象が起こっているケースで何が出力されるでしょうか。 if ("$In_data[0]" eq "$bed_data[3]" ){   print "In_data =", unpack("C*", $In_data[0]), "\n";   print "bed_data=", unpack("C*", $bed_data[3]), "\n"; print DATA "@bed_data\n"; }
ssasaki

2019/04/24 07:08

ちなみに、$In_line と $bed_line の2カ所に追加しています。
KojiDoi

2019/04/24 07:23

>ssasakiさん どうせならこの方がいいかと。 s/(\x0a|\x0d)*$//; perlの\nは論理的な改行なので厳密なことを言えばプラットフォームによって具体的に指し示すものが変わってきます。色んな人がWinやらMacやらlinuxやらいろんな環境で作たファイルを集めてきて作業しなければならないときはコードを直接指定して全削除してしまったほうが簡単確実です。
ssasaki

2019/04/24 07:29

なるほど。たしかにそれで chomp 無しにした方が確実ですかね。
ransky

2019/04/24 07:35

ssasaki様、KojiDoi様、ありがとうございます。 提案していただいたものを試してみて結果を報告いたします。
ransky

2019/04/24 09:09

ssasaki様、KojiDoi様、 ENST_list.txtをVisual Studio Codeで開くとLFと表示されていたので、LFだと信じ込んでしまっていましたが、 od -cで調べると、\r\nと\nが混ざっていました。 別のファイル(ENST_original.txt)からperlコードでENST_list.txtを出力したのですが、ENST_original.txtの改行コードは全て\r\nになっていました。 ENST_list.txtの改行コードをLFに統一することで、私の書いた最初のコードでも動きました。 また、ENST_list.txtの改行コードに\r\nと\nが混ざった状態でも、 s/\r\n/\n/; s/(\x0a|\x0d)*$//; は共に期待した出力ができました。 さらに、if blockをKojiDoi様のコードに置き換えた場合 1) ENST_list.txtの改行コードに\r\nと\nが混ざった状態 In_data =697883844848484848525056484955 bed_data=697883844848484848525056484955 In_data =697883844848484848535053505453 bed_data=697883844848484848535053505453 2) ENST_list.txtの改行コードに\r\nと\nが混ざった状態で、最終行の改行記号をエディタで削除した場合 In_data =697883844848484848525457525652 bed_data=697883844848484848525457525652 In_data =697883844848484848525056484955 bed_data=697883844848484848525056484955 In_data =697883844848484848535053505453 bed_data=697883844848484848535053505453 3) ENST_list.txtの改行をLFに修正した場合 In_data =697883844848484848525457525652 bed_data=697883844848484848525457525652 In_data =697883844848484848525056484955 bed_data=697883844848484848525056484955 In_data =697883844848484848535053505453 bed_data=697883844848484848535053505453 となりました。私の現在の知識では s/(\x0a|\x0d)*$//; や if blockの書き換えで出力されたものの意味が理解できないので学習いたします。 改行コード確認の重要性がわかりました。 お二人のご助言に感謝いたします。 ありがとうございました。
ssasaki

2019/04/25 01:39

解決して良かったです。 VSCode って勝手に改行コード変えちゃうのですね。知らなかったです(^_^;)
guest

0

1行目;A\n
2行目;B

という出力を行えばいいんじゃないでしょうか

投稿2019/04/24 01:00

y_waiwai

総合スコア87747

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

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

ransky

2019/04/24 05:18

ご回答ありがとうございます。 ご指摘のように、 最後の行の前まで改行記号をつけて、最終行だけ改行記号をつけない。 または 最終行も含めて改行記号をつけて、あとで最終行だけ改行記号を削除するで良さそうですので、考えてみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問