C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[1]: C# seekでの読み込み位置変更について


(過去ログ 150 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■87614 / inTopicNo.1)  C# seekでの読み込み位置変更について
  
□投稿者/ mau (1回)-(2018/06/14(Thu) 02:00:44)

分類:[C#] 

VisualStudio Community 2017
を使用しています。

以下のデータとソースを作成しました。
予想では、読み込みデータは1行目、5行目、10行目と
出力してほしかったのですが、
1,2,3行目が出力してしまいます。



sample.csvの中身
1,100
2,200
3,300
4,400
5,500
6,600
7,700
8,800
9,900
10,1000


        private void button1_Click(object sender, EventArgs e)
        {
/**/
            // sampleファイルオープン
            var fs = new FileStream(@"C:\Users\mau\sample.csv", FileMode.Open, FileAccess.Read);
            var sr = new StreamReader(fs, Encoding.Default);

            // 書き込み用ファイルオープン
            StreamWriter sw = new StreamWriter(@"C:\Users\mau\sample_new.csv", true, Encoding.GetEncoding("shift_jis"));
            sw.WriteLine("fpos,no,row");

            //現在のfsの位置
            long fpos = fs.Position;
            // 1行目読み込み
            var buf = sr.ReadLine();
            sw.WriteLine(fpos +","+ buf +","+ fs.Position);
            byte[] st = new byte[20];

            // 5行目読み込み
            fpos = 0;
            fpos = fpos + (buf.Length + 1) * 5;
            fs.Seek(fpos, SeekOrigin.Begin);
            var buf = sr.ReadLine();

            buf = sr.ReadLine();0
            sw.WriteLine(fpos + "," + buf + "," + fs.Position);

            // 10行目読み込み
            fpos = 0;
            fpos = fpos + (buf.Length + 1) * 10;
            fs.Seek(fpos, SeekOrigin.Begin);
            buf = sr.ReadLine();
            sw.WriteLine(fpos + "," + buf + "," + fs.Position);
            fs.Close();
            sr.Close();
            sw.Close();
        }
    }
}

引用返信 編集キー/
■87615 / inTopicNo.2)  Re[1]: C# seekでの読み込み位置変更について
□投稿者/ Azulean (955回)-(2018/06/14(Thu) 06:25:11)
No87614 (mau さん) に返信
> 以下のデータとソースを作成しました。
> 予想では、読み込みデータは1行目、5行目、10行目と
> 出力してほしかったのですが、
> 1,2,3行目が出力してしまいます。

このコードのように Seek すれば、さすがに 1, 2, 3 行目が出るということはなさそうですが…。
提示コードはコンパイルエラーになるようなものなので、実際にはコードの変更が反映されていないものを実行していませんか?

さて、データの中には「1,100」や「10,1000」というように一行の文字数が異なる箇所があります。
現実のファイル読み込みでは行単位でシークするということは困難です。
お手軽に実装するなら、「3 回 ReadLine して戻り値を使わず(読み飛ばして)、その次の ReadLine を 5 行目として受け入れる」みたいなコードにした方が確実でしょう。
引用返信 編集キー/
■87616 / inTopicNo.3)  Re[2]: C# seekでの読み込み位置変更について
□投稿者/ Hongliang (647回)-(2018/06/14(Thu) 09:22:26)
回答自体はAzuleanさんの仰るようにReadLineで読み飛ばすようにするでいいとして、なぜSeekのあとのReadLineが思ったような結果を返さないのかという点について。

Streamから1バイトずつ読み取るという作業はパフォーマンスがよろしくないので、StreamReaderは内部にバッファを持ち、ある程度まとめてStreamから読み取ります。
ちなみにバッファサイズはコンストラクタで指定できますが最小で128バイト。既定は1024バイト。
バッファが足りている間はStreamからの読み取りを行わないので、StreamをいくらSeekしたところでそれとは無関係に、事前に読み込んだバッファから文字列を作って返してきます。
引用返信 編集キー/
■87617 / inTopicNo.4)  Re[1]: C# seekでの読み込み位置変更について
□投稿者/ ????? (1回)-(2018/06/14(Thu) 09:34:19)
2018/06/14(Thu) 09:35:42 編集(投稿者)
2018/06/14(Thu) 09:35:25 編集(投稿者)

削除します。すみません。
引用返信 編集キー/
■87618 / inTopicNo.5)  Re[2]: C# seekでの読み込み位置変更について
□投稿者/ にゃるら (23回)-(2018/06/14(Thu) 09:36:14)
No87614 (mau さん) に返信

>
> // 5行目読み込み
> fpos = 0;
> fpos = fpos + (buf.Length + 1) * 5;

仮に1行分のサイズが「あっている」としたとして、5倍ということは6行目の先頭になりますね。
10行目の読み込みも同じことが言えます。

> fs.Seek(fpos, SeekOrigin.Begin);
> var buf = sr.ReadLine();

StreamReaderは文字エンコードを考慮してキャッシュして読み込みしてるのかしら?
だとすると、ストリームのポジションを移動させてもキャッシュは消えていないことになると予想。
だからSeekのあとに、StreamReaderのDiscardBufferedDataメソッドを読んでみてはいかがでしょうか?
引用返信 編集キー/
■87628 / inTopicNo.6)  Re[1]: C# seekでの読み込み位置変更について
□投稿者/ WebSurfer (1519回)-(2018/06/14(Thu) 12:15:32)
No87614 (mau さん) に返信

目的は行を指定して CSV ファイルの指定行のデータを読みたいということで、seek を使う
のはそのための手段として質問者さんが考えたことで、目的ではない(手段は何でもよい)
のであれば・・・

CSV ファイルからは一旦全行読んでしまい、それを DataTable とか List<T> 型のオブジェ
クトに格納し、それから行を指定してデータを取得するという方法ではいかがですか?

その際、CSV ファイルを読むには TextFieldParser などの既存の CSV パーサーを使うこと
をお勧めします。

CSV ファイルの文字コード(Shift_JIS, UTF-8, UTF-16 BE, UTF-16 LE)の違いによる
文字化けとか、フィールの中にカンマがあって "1,000" となっているような場合の対応
が楽になりますので。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -