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

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

ただいまの
回答率

88.64%

C#でCSVデータを配列に読み込もうとするとNullReferenceExceptionが出る

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,861

SATSUKI.

score 20

 概要

c#でフォームアプリを作っています。その中でcsvファイルのデータを配列に読み込む処理があるのですが、その処理をしている部分でNullReferenceExceptionが出てしまいます。おそらくインスタンスの生成がうまくいってないと思うのですが、なぜなのかわからず困っています。c#は勉強している最中で、そこら辺がまだよく理解できてないのでお力を貸していただきたいです。

 フォームアプリの仕様

8個のcsvファイル(データは数値、行と列のデータの数は揃っていて8個のファイルすべて同じデータ数、空データなし)をopenFileDialogで開きます。calcボタンが押されるとそれらのデータを一旦配列に読み込み、計算処理をした結果を新しい2つのcsvファイルに書き込んで保存します。

 ソースコード

以下該当部分の抜粋です。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


//namespaceなど中略

        private void buttonCalc_Click(object sender, EventArgs e)   //calcボタンが押されたときに発生するイベント
        {
            if (openFileDialog1.FileNames.Length == 8)  //ファイルが8つ選択されていたら
            {
                StreamReader[] readCSVfile = new StreamReader[8];   //ファイルから読み込んでデータを格納するためのstreamオブジェクト
                for (int n = 0; n < 8; n++)     //ファイル8個開く
                {
                    readCSVfile[n] = new StreamReader(openFileDialog1.FileNames[n], Encoding.GetEncoding("Shift_JIS")); //データが入っているcsvファイルを開く
                }
                string calculated1FilePath = textBoxSaveFolder.Text + "\\calculated1.csv";  //計算結果を保存するcsvファイルのパス
                string calculated2FilePath = textBoxSaveFolder.Text + "\\calculated2.csv"; //計算結果を保存するcsvファイルのパス
                StreamWriter calculated1CSV = new StreamWriter(calculated1FilePath); //calculated1のデータを書き込むcsvファイルを生成
                StreamWriter calculated2CSV = new StreamWriter(calculated2FilePath); //calculated2のデータを書き込むcsvファイルを生成


                try
                {
                    for (int i = 0; readCSVfile[0].EndOfStream == false; i++)  //行を走査
                    {
                        string[,] dataPixelLine = new string[8, readCSVfile[0].ReadLine().Split(',').Length];  //データ一行分をカンマで区切ったものを8つ(ファイル8つあるので)格納するためのstring型二次元配列//添字の0次元目はファイル番号、1次元目は列番号

                        for (int n = 0; n < 8; n++)     //ファイルを8個同時に開いて計算する
                        {
                            for (int s = 0; s < readCSVfile[n].ReadLine().Split(',').Length; s++) //列を走査
                            {
                                dataPixelLine[n, s] = String.Copy(readCSVfile[n].ReadLine().Split(',')[s]);  //一行分の文字列をカンマで区切って一列ずつstring型2次元配列に格納     -------ここでNullReferenceExceptionが出る-------
                            }
                        }



                        string[] calculated1String = new string[dataPixelLine.GetLength(0)];  //計算したcalculated1の値を一行分記憶するためのstring型配列
                        string[] calculated2String = new string[dataPixelLine.GetLength(0)];  //計算したcalculated2の値を一行分記憶するためのstring型配列

                        //ここに計算処理が入る
                        //計算結果がcalculated1Stringとcalculated2Stringに格納される

                        string calculated1Line = string.Join(",", calculated1String);  //calculated1String[]の各要素に格納されている文字を、カンマ区切って一つの文字列にする
                        string calculated2Line = string.Join(",", calculated2String);  //calculated2String[]の各要素に格納されている文字を、カンマ区切って一つの文字列にする
                        calculated1CSV.WriteLine(calculated1Line);  //新しいcsvファイルに一行書き込む
                        calculated2CSV.WriteLine(calculated2Line);  //新しいcsvファイルに一行書き込む
                    }
                }
                finally
                {
                    for (int n = 0; n < 8; n++)  //ファイルクローズ処理
                    {
                        readCSVfile[n].Close();
                    }
                }
                MessageBox.Show("処理終了");
            }
            else
            {
                MessageBox.Show("ファイルを8つ選択してください");
            }
        }

 スタックトレース

************** 例外テキスト **************
System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません。
   場所 calc_csv.Form1.buttonCalc_Click(Object sender, EventArgs e) 場所 CUsers¥myPC¥Visual Studio 2017¥Projects¥CWindows Form Apprications¥calc csv¥calc csv¥Form1.cs:行 77
   場所 System.Windows.Forms.Control.OnClick(EventArgs e)
   場所 System.Windows.Forms.Button.OnClick(EventArgs e)
   場所 System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   場所 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   場所 System.Windows.Forms.Control.WndProc(Message& m)
   場所 System.Windows.Forms.ButtonBase.WndProc(Message& m)
   場所 System.Windows.Forms.Button.WndProc(Message& m)
   場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   場所 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

よろしくお願いします。


 バージョンなど

Windows10 Pro 64bit
Visual Studio 2017 community
.NET Framework 4.7
C# 7.0

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • skitoy4321

    2017/07/24 14:17

    スタックトレース含めた例外の詳細内容を記載すれば、適切な回答が得られるかもしれません。

    キャンセル

  • SATSUKI.

    2017/07/24 14:46

    ご指摘ありがとうございます。追記しました。

    キャンセル

  • koguma98

    2017/07/24 14:47

    Form1.cs の 77 行目はどの行でしょう?

    キャンセル

  • SATSUKI.

    2017/07/24 14:53

    ご指摘ありがとうございます。dataPixelLine[n, s] = String.Copy(readCSVfile[n].ReadLine().Split(',')[s]); の行です。ソースコード中にコメントで"-------ここでNullReferenceExceptionが出る-------"と記述してある行です。

    キャンセル

回答 3

+1

とりあえず、一度、splitした情報を配列に入れてから処理しては如何でしょうか?

string[,] dataPixelLine = new string[8, readCSVfile[0].ReadLine().Split(',').Length];  //データ一行分をカンマで区切ったものを8つ(ファイル8つあるので)格納するためのstring型二次元配列//添字の0次元目はファイル番号、1次元目は列番号
//1行目の1列目が必ず読み飛ばされますが…

for (int n = 0; n < 8; n++)     //ファイルを8個同時に開いて計算する
{
    string line = ""; 
    while ((line = readCSVfile[n].ReadLine()) != null) //ファイルの内容を全て読込む
    {
        Array strArray = line.Split(',');
        for (int s = 0; s < strArray.Length; s++) //列を走査
        {
            dataPixelLine[n, s] = (string)strArray.GetValue(s);  //一行分の文字列をカンマで区切って一列ずつstring型2次元配列に格納     -------ここでNullReferenceExceptionが出る-------
        }
    }
}

その他諸々、気になったこと(本筋と関係ないので、読み飛ばしてもらってもOKです)

  • 出力ファイルがCloseされていないので、フォームを閉じずに2回目に実行するとエラーになります。
  • コメントにも書きましたが、必ず1行目の1列目が読み飛ばされてしまいます。
  • このロジックだと1ファイルに複数行があった場合でも、最終行のデータのみが変数に保存されます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/26 18:10

    具体的な改善策まで提示してくださり、ありがとうございます。「その他諸々、気になったこと」もたいへん参考になりました。

    キャンセル

checkベストアンサー

0

こんにちは。

終端に到達したStreamでReadLineを呼ぶとnullが返るので、nullに対してsplitを呼んでいるのが直接的な原因です。

ちなみに、C#のStreamはReadLineメソッドを呼ぶ度に次の行にシークするので、

for (int n = 0; n < 8; n++)     //ファイルを8個同時に開いて計算する
{
    for (int s = 0; s < readCSVfile[n].ReadLine().Split(',').Length; s++) //列を走査
    {
        dataPixelLine[n, s] = String.Copy(readCSVfile[n].ReadLine().Split(',')[s]);  //一行分の文字列をカンマで区切って一列ずつstring型2次元配列に格納     -------ここでNullReferenceExceptionが出る-------
    }
}


この部分のReadLineについて、内部のforループで条件判定を行う度に一行データを読み飛ばしている、カンマでSplitした一つを読む度に残りのデータを捨てているため、意図した動作にはなっていないと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/26 18:10

    ReadLineを呼び出すたびに次の行に遷移してしまい、意図したのとは違うタイミングでstreamが終端に到達していたのですね。原因がわかってよかったです。ありがとうございます。

    キャンセル

0

同時に処理しているつもりなのかもしれませんが、ここで最初のファイルしかチェックしていません。

for (int i = 0; readCSVfile[0].EndOfStream == false; i++)  //行を走査

単純にSplitするだけでいい要件なのであれば、Streamを使うよりもFile.ReadAllLines()で一気に読みこんだほうがいいかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/26 18:11

    File.ReadAllLines()というメソッドがあるのですね、初めて知りました。使わせていただきます。

    キャンセル

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

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る