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

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

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

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

Q&A

解決済

2回答

1840閲覧

C# フォルダ内の複数のdatファイル内から必要なキーを抽出し1つのCSVファイルにリストとして書き出す

ShinyaKojima

総合スコア18

C#

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

0グッド

0クリップ

投稿2019/04/03 09:01

前提・実現したいこと

初めて質問致します。至らない点があれば申し訳ございません。

最近C#を勉強し始めた初心者です。
表題の通り、あるフォルダ内にある複数のdatファイルの中から必要なキーを抽出してそれをまとめたCSVファイルを作成したいと考えています。
別の所で質問をして[Nugetパッケージの管理]から CsvHelper をインストール。

複数ファイルを読み込んで書き出し用リストに突っ込めば出来る
という風に教えていただいたのですが。
使い方を自分なりに調べてみましたがなかなか理解できず使いこなせていない状態です。
絶対パスで@"C:\検証用\検証データ\MLOG_RCVの中にワイルドカードでdatファイルを全て読み込もうとして@"C:\検証用\検証データ\MLOG_RCV*.datのようにしたのですがハンドルされていない例外というエラーが発生しました。自分なりに調べて*が無効な文字ということは分かったのですが、この場合全てのファイルを読み込むためにはどのように書いたら良いのか分かりません。
下記のソースコードも殆ど理解できていないままなので間違いだらけかもしれません。

datファイルの中身は以下のようになっておりフォルダ内に数万件あります。
datファイル(カンマ区切りの4~5列、約800行程度)
ProcessData,LOT_ID,3,AP0076686.00C,
ProcessData,LOT_ID_SUB,3,AP0076686.00,
ProcessData,LOT_NO,3,AP0076686,
ProcessData,WAFER_ID,3,AP0076686.19,
ProcessData,WAFER_NO,1,19,
ProcessData,PRODSPEC_ID,3,T5DH20001-00001.00,
ProcessData,PRODGRP_ID,3,T5DH2,
ProcessData,PRODGRP_BIND,3,T5DH2,
ProcessData,MAIN_MAINPD_ID,3,A6L511NY.00,
ProcessData,MAINPD_ID,3,A6L511NY.00,
ProcessData,FLOW_TYPE,3,Main,
ProcessData,FLOW_TYPE_NO,1,1,
ProcessData,D_SEQNO,1,169,
ProcessData,OP_NO,3,PNH PEP.MA1,
ProcessData,OP_NO_NAME,3,本処理,
ProcessData,PD_IDENT,3,PPNHIMA1.00,
ProcessData,PD_IDENT_NAME,3,PEP,
ProcessData,EQP_GROUP_CODE,3,PKRF,
ProcessData,EQP_GROUP_NAME,3,KrF SCANNER(SK3000 + ES5),
ProcessData,EQP_GROUP_BIND,3,PKRF,
ProcessData,EQP_ID,3,PKRF004,
ProcessData,PH_RECIPE_ID,3,PES5MIX,
ProcessData,RCP_NAME_SPACE,3,PEPMA,
ProcessData,LC_RECIPE_ID,3,PKRF.01,
ProcessData,RECIPE_ID,3,PEPMA.PES5MIX,
ProcessData,S_DATE,4,2019/01/24 12:47:09,
ProcessData,E_DATE,4,2019/01/24 12:47:51,
ProcessData,CAST_ID,3,PA0-00349,
ProcessData,SLOT_NO,1,19,

この中から
EQP_IDの時PKRF004
LOT_IDの時AP0076686.00C
Wafer_IDの時AP0076686.19
S_DATEの時2019/01/24 12:47:09
のようにデータを取得し
PKRF004 AP0076686.00C AP0076686.19 2019/01/24 12:47:09
の並びで何100行のようにリスト化されたcsvファイルを作るのが最終的な目標です。
イメージとしては
1.1ファイルの読み込み、解析、必要情報の保持。
2.ファイルの書き出し。
3.ファイルの読み込み、解析、必要情報の保持。
4.ファイルの書き出し。(追記)
というように繰り返し処理を行えばよいと思うのですが  
ロジックの書き方などを理解できていない状態です。

ソースコードの誤りの修正も含めC#に詳しい方、どうかご教授をお願い致します。

発生している問題・エラーメッセージ

エラーメッセージ ハンドルされていない例外 System.ArgumentException:'パスに無効な文字が含まれています。'

該当のソースコード

C#

using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace ConsoleApp1 { class DatRowValues { public string ProcessData { get; set; } public string KeyValue { get; set; } public int IntValue { get; set; } public string StringValue { get; set; } } class NewDatRowValues { public string EqpId { get; set; } public string LotId { get; set; } public string WaferId { get; set; } public DateTime SDate { get; set; } } class NewDatRowValuesMapper : CsvHelper.Configuration.ClassMap<NewDatRowValues> { public NewDatRowValuesMapper() { Map(x => x.EqpId).Index(0); Map(x => x.LotId).Index(1); Map(x => x.WaferId).Index(2); Map(x => x.SDate).Index(3).TypeConverterOption.Format("yyyy/MM/dd HH:mm:ss"); } } class Program { static void Main(string[] args) { // 書き出し用の入れ物を用意 var writeDatList = new List<NewDatRowValues>(); // 読み込み using (var sr = new StreamReader(@"C:\検証用\検証データ\MLOG_RCV*.dat", System.Text.Encoding.GetEncoding("shift_jis"))) using (var inputDat = new CsvHelper.CsvReader(sr)) { inputDat.Configuration.HasHeaderRecord = false; // 必要なキーとなる行のみ抽出 var dat = inputDat.GetRecords<DatRowValues>(); var targetRows = dat.Where(r => r.KeyValue == "EQP_ID" || r.KeyValue == "LOT_ID" || r.KeyValue == "WAFER_ID" || r.KeyValue == "S_DATE"); // それぞれの値を格納 var newRow = new NewDatRowValues(); foreach (var row in targetRows) { if (row.KeyValue == "EQP_ID") { newRow.EqpId = row.StringValue; } if (row.KeyValue == "LOT_ID") { newRow.LotId = row.StringValue; } if (row.KeyValue == "WAFER_ID") { newRow.WaferId = row.StringValue; } if (row.KeyValue == "S_DATE") { newRow.SDate = DateTime.Parse(row.StringValue); } } writeDatList.Add(newRow); } // 書き出し using (var sw = new StreamWriter("list.csv")) using (var outputDat = new CsvHelper.CsvWriter(sw)) { outputDat.Configuration.HasHeaderRecord = false; outputDat.Configuration.RegisterClassMap<NewDatRowValuesMapper>(); outputDat.WriteRecords(writeDatList); } } } }

試したこと

*が無効な文字ということなので1つのファイル名で直接入力してみましたが
ハンドルされていない例外
CsvHelper.TypeConversion.TypeConverterException: 'The conversion cannot be performed.
Text: '91G208446-19'
MemberType: System.Int32
TypeConverter: 'CsvHelper.TypeConversion.Int32Converter''
というメッセージが表示されました。

補足情報(FW/ツールのバージョンなど)

開発環境Visual Studio2017

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

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

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

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

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

guest

回答2

0

ベストアンサー

csharp

1using (var sr = new StreamReader(@"C:\検証用\検証データ\MLOG_RCV*.dat", System.Text.Encoding.GetEncoding("shift_jis")))

これは、C:\検証用\検証データ\MLOG_RCV*.dat という名前のファイルを読み取ろうとしています。
しかし * はファイル名として使用できない文字なので、System.ArgumentException:'パスに無効な文字が含まれています。' が発生しています。


csharp

1foreach (string fileName in Directory.GetFiles(@"C:\検証用\検証データ\MLOG_RCV", "*.dat")) 2using (var sr = new StreamReader(fileName, System.Text.Encoding.GetEncoding("shift_jis")))

このように変えてみてはどうでしょうか。
System.IO.Directory.GetFiles で特定のフォルダに含まれるdatファイルの一覧を取得し、foreach でループ処理しています。
Directory.GetFiles Method (System.IO) | Microsoft Docs


以下は余談です。

下記のソースコードも殆ど理解できていないままなので間違いだらけかもしれません。

理解しましょう。
理解しないまま書いたコードは、思わぬバグを引き起こしかねません。
何事も一足飛びにはいかないものですから、あせらずに一文ずつ丁寧に理解すると良いと思います。

投稿2019/04/03 09:25

alg

総合スコア2019

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

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

ShinyaKojima

2019/04/04 00:02

using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace ConsoleApp1 { class DatRowValues { public string ProcessData { get; set; } public string KeyValue { get; set; } public int IntValue { get; set; } public string StringValue { get; set; } } class NewDatRowValues { public string EqpId { get; set; } public string LotId { get; set; } public string WaferId { get; set; } public DateTime SDate { get; set; } } class NewDatRowValuesMapper : CsvHelper.Configuration.ClassMap<NewDatRowValues> { public NewDatRowValuesMapper() { Map(x => x.EqpId).Index(0); Map(x => x.LotId).Index(1); Map(x => x.WaferId).Index(2); Map(x => x.SDate).Index(3).TypeConverterOption.Format("yyyy/MM/dd HH:mm:ss"); } } class Program { static void Main(string[] args) { // 書き出し用の入れ物を用意 var writeDatList = new List<NewDatRowValues>(); // 読み込み foreach (string fileName in Directory.GetFiles(@"C:\検証用\検証データ\MLOG_RCV", "*.dat")) using (var sr = new StreamReader(fileName, System.Text.Encoding.GetEncoding("shift_jis"))) using (var inputDat = new CsvHelper.CsvReader(sr)) { inputDat.Configuration.HasHeaderRecord = false; // 必要なキーとなる行のみ抽出 var dat = inputDat.GetRecords<DatRowValues>(); var targetRows = dat.Where(r => r.KeyValue == "EQP_ID" || r.KeyValue == "LOT_ID" || r.KeyValue == "WAFER_ID" || r.KeyValue == "S_DATE"); // それぞれの値を格納 var newRow = new NewDatRowValues(); foreach (var row in targetRows) { if (row.KeyValue == "EQP_ID") { newRow.EqpId = row.StringValue; } if (row.KeyValue == "LOT_ID") { newRow.LotId = row.StringValue; } if (row.KeyValue == "WAFER_ID") { newRow.WaferId = row.StringValue; } if (row.KeyValue == "S_DATE") { newRow.SDate = DateTime.Parse(row.StringValue); } } writeDatList.Add(newRow); } // 書き出し using (var sw = new StreamWriter("list.csv")) using (var outputDat = new CsvHelper.CsvWriter(sw)) { outputDat.Configuration.HasHeaderRecord = false; outputDat.Configuration.RegisterClassMap<NewDatRowValuesMapper>(); outputDat.WriteRecords(writeDatList); } } } } という風に変更しました。 エラーというより foreach (var row in targetRows) の部分で ハンドルされていない例外 でCsvHelper.TypeConversion.TypeConverterException: 'The conversion cannot be performed. Text: '91G208446-19' MemberType: System.Int32 TypeConverter: 'CsvHelper.TypeConversion.Int32Converter'' というように表示されています。 foreachで*.datを取ってくるところは分かりやすかったです。 コメント部分も自分が分かってるつもり?くらいの内容なので間違えているかもしれません。 最後の書き出しの部分についてですが using (var sw = new StreamWriter("list.csv"))という風に記述してますが フォルダを指定する場合は(@"C:\検証用\検証データ\MLOG_RCV"\BACKUP","list.csv")のような書き方で良いのでしょうか? 引き続き宜しくお願い致します。
YAmaGNZ

2019/04/04 00:32

ハンドルされていない例外はintの項目のところにintではないデータがあるのでintに変換しようとして失敗しています。 対象の行(91G208446-19が含まれる行)が正しいのか確認してはどうでしょう。
ShinyaKojima

2019/04/04 00:47

class DatRowValues { public string ProcessData { get; set; } public string KeyValue { get; set; } public int IntValue { get; set; } public string StringValue { get; set; } } の public int IntValue { get; set; } を public string IntValue { get; set; } に変更したら思った通りの動きになりました。 ありがとうございました。
ShinyaKojima

2019/04/04 00:52

最後の質問なのですが // 書き出し using (var sw = new StreamWriter("list.csv")) 部分で書き出すファイルの絶対パスを指定する場合はどのように記述したらよいのでしょうか? 宜しくお願い致します。
ShinyaKojima

2019/04/04 00:54

失礼しました。 ページを更新する前にコメントをしてしまいました。 御教授ありがとうございます。
ShinyaKojima

2019/04/04 01:11

上記の通りに実行したら正しく動作致しました。 御教授いただいた皆様大変ありがとうございました。
YAmaGNZ

2019/04/04 01:20

IntValueはstringでいいのですか? stringにすれば例外はでないでしょうが、データとしてその部分は数字のみが正しいのであれば、データが間違っていることになります。 数字以外のデータも存在するのであればstringでいいでしょうが、最初そのように設計したのは、その部分が数字しかないからではないのですか? 間違ったデータがあった場合、プログラムが例外で終了するのはダメですが、異常なデータでも処理するのは間違っています。 try-catchなどで、例外が発生した場合はエラーが出たことをユーザーに通知すべきです。
ShinyaKojima

2019/04/08 09:34

返信遅くなり申し訳ありません。データとしてその部分を確認した際に数字以外にAやBやCなど文字列が含まれていることが分かりstringが正しいようです。最初の設計の際によく中身を確認しておらず早とちりしていました。今後気を付けます。 try-catchについても勉強してより良いコードを書けるように頑張ります。
guest

0

投稿2019/04/03 09:15

kiichi54321

総合スコア1984

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問