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

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

新規登録して質問してみよう
ただいま回答率
85.46%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

1回答

1123閲覧

Stream​Reader.​Read​Lineで読み取ったlogファイルの内容を逆順で読み込みたい

ookura

総合スコア27

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2021/05/26 08:25

編集2021/05/26 11:11

表題の通りなのですがlogファイルの中身を逆の順番で読み取ろうとしています。
いくつか試してみたのですがうまく再現できなかった為、お知恵をお借り出来たらと思います。
・試してみたこと
sr.ReadLineを⇒sr.ReadLine().reverseに

System.Linq.Enumerable+ReverseIterator`1[System.Char]と表示されるだけ

sr.Peek()をsr.Peek().reverse()

エラー

リストに一度すべて格納してreverse?そのほかに逆順でループ等々ありそうなのですが
どれが最適解かいまいちわかりません。
お手数おかけしますがご教示いただけますと幸いです。

C#

1 private void Line_read() 2 { 3 4 try 5 { 6 string Path = @"C:\Users\user\Documents\test.log"; 7 using (FileStream fs = new FileStream(Path, 8 FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 9 { 10 11 using (TextReader sr = new StreamReader(fs, 12 Encoding.GetEncoding("UTF-8"))) 13 { 14 String line; 15 // Read and display lines from the file until the end of 16 // the file is reached. 17 while (sr.Peek() > -1) 18 { 19 //※逆順で一行読み込んで表示する 20 Debug.WriteLine(sr.ReadLine()); 21 } 22 23 sr.Close(); 24 25 26 } 27 } 28 } 29 catch (Exception e) 30 { 31 // Let the user know what went wrong. 32 Debug.WriteLine("The file could not be read:"); 33 Console.WriteLine(e.Message); 34 } 35 }

追記:
記載漏れがありました。すみません。
逆順で1行ずつ回しながらその行の検索を行う、という処理を挟むまでが全容なのですが
リストにすべて格納するというと、全体を通常の順序で配列に取り込み⇒reverseして
配列を逆に、という形になると思います。
当然そこまでは思いつくのですが、そうなると
一度配列に入れる正順ループを行った後、リバースして
さらにループと、2回ループするようで不格好かと思います。
1度のループで配列に取り込まず最初から
逆順に読み込もうとして方法がうまく思いつかない、という旨の質問となります。
コメント頂いた方々には申し訳ありません。
もしよろしければ再度ご教示いただけますと幸いです。
(2度ループするしか方法がないというのであればそれはそれで構いません)
不勉強かつ言葉足らずでで申し訳ない限りです。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/05/26 08:33 編集

> リストに一度すべて格納してreverse?そのほかに逆順でループ等々ありそうなのですが どれが最適解かいまいちわかりません。 お手数おかけしますがご教示いただけますと幸いです。 こちらの方法で機能としては実現できませんか?
neconekocat

2021/05/26 08:51 編集

普通に実装するなら全てを読み込んで行データの配列にするのが正しいと思います。 ただ、増えていくログを監視して動的に追いたい(tail -fやless +f的なもの)と考えているのであれば、 ファイルの改行コードを検索してそこへSeek→そこからReadLineという形での実装が一番かなと思います
dodox86

2021/05/26 09:59

先に皆さんにコメントいただいているように、 > リストに一度すべて格納してreverse?そのほかに逆順でループ等々ありそうなのですが どれが最適解かいまいちわかりません。 「どれが最適解か」などと一番良い方法を最初からやろうとしたり教えてもらおうとするのではなく、最初は愚直でも自分でできる簡単な方法でやればよいです。
退会済みユーザー

退会済みユーザー

2021/05/26 12:12

> 普通に実装するなら全てを読み込んで行データの配列にするのが正しいと思います。 それなら質問者さんは問題なく実装できるのですか? もし、今の質問者さんのスキルでそれができないということなら、それができてから話をした方が良いのでは?
neconekocat

2021/05/26 15:16

不格好の意味がよくわかりませんが、何が何でも後ろから1行ずつ読みたい、というのであればFileStreamを使ってそういう処理を自分で書くしか無いです。ただし1行読むのにかかる時間はReadLineより長くなるため、全行を処理するつもりなら確実にパフォーマンスが落ちます。(巨大なファイルの最後の数行のみを処理したい等の用途であれば早くなります) 最適解を知りたいのであれば、どういう用途なのかを明確にしなければ誰も答えられません。
ookura

2021/05/26 23:10

neconekocat様 ご回答ありがとうございます。 ご教示いただいたようにFileStreamで処理を作成する用途が一番希望に近しいかと思いました。 一度、その方針で調べてみます。 ベストアンサーに挙げたいのですが、追記・修正依頼でのご回答をいただいていたので、よろしければ回答として再投稿いただくことは可能でしょうか?
dodox86

2021/05/27 01:05

どうもややこしい方向に行っている気がするのですが、行というのはファイルを読んで初めて意識できることで、ファイルの最後の行から読むにはファイルを全部バッファに読むか、あるいは最後の方と思わしき部分(ファイルの後ろの方だけ)をバッファに読んで、直前の行('\n')の位置まで出力位置を逆に移動するしかありません。File.ReadLines()などを使えば行の意識を.NETのクラスライブラリがやってくれるのでそれを使うのが簡単、と言う話です。「FileStreamやTextReaderクラスを使う」縛りでもあるのでしょうか。
neconekocat

2021/05/27 03:20

質問者さんの別の質問を見た感じ、ログファイルを監視して都度最終行を取得する必要があるので、本当の目的はパフォーマンス向上をしたい、というXY問題ではないかと疑っています。 残念ながら回答が得られていない為、推測止まりですが。
guest

回答1

0

ベストアンサー

あくまでも例ですが、後ろから1行ずつ読みたいのであればこんな感じの処理が必要になります。
多くの人が指摘していますが、実装する上でこんなモノが本当に必要かどうかはよく考えてください。
特殊な状況を除けばFile.ReadLines().Reverse()等で事足りることが大半だと思います。

なお、このサンプルの1行あたりの処理は遅いです。ファイルによってはTake等で適切に処理しないと地獄を見ます。

C#

1private IEnumerable<string> ReverseReadLines(string path) 2{ 3 using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); 4 5 long current; 6 long start; 7 long end; 8 byte[] buff = new byte[100]; 9 byte[] line; 10 11 fs.Seek(-1, SeekOrigin.End); 12 fs.Read(buff, 0, 1); 13 // ファイルが改行で終わっている場合は飛ばしておく 14 end = buff[0] == 0x0A ? fs.Seek(-2, SeekOrigin.End) : fs.Seek(0, SeekOrigin.End); 15 current = fs.Position; 16 17 while (current > 0) 18 { 19 int size = (int)Math.Min(current, buff.Length); 20 fs.Seek(-size, SeekOrigin.Current); 21 fs.Read(buff, 0, size); 22 23 for (int i = 0; i < size; i++) 24 { 25 if (buff[size - i - 1] == 0x0A) // LF 26 { 27 start = fs.Seek(-i, SeekOrigin.Current); 28 line = new byte[end - start]; 29 fs.Read(line); 30 31 yield return System.Text.Encoding.UTF8.GetString(line); 32 33 // 前の行へ 34 end = fs.Seek(-line.Length - 2, SeekOrigin.Current); // CRLF決め打ち 35 break; 36 } 37 } 38 39 if (current == fs.Position) 40 { 41 // 読み込んだ範囲内に改行がなかった場合は更に前方を検索 42 fs.Seek(-size, SeekOrigin.Current); 43 } 44 current = fs.Position; 45 } 46 47 // 1行目 48 line = new byte[end]; 49 fs.Read(line); 50 51 yield return System.Text.Encoding.UTF8.GetString(line); 52}

投稿2021/05/27 15:27

neconekocat

総合スコア443

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問