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

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

ただいまの
回答率

90.04%

数万件あるファイルの中身を読み込みある特定の文字列が同じものだけをテキストファイルに書き出す

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 325

ShinyaKojima

score 12

いつもお世話になっております。

以前質問した内容に追加するような形になるのですが、数万件あるファイルからある文字列を探すのですが、基本は1つのファイルの中に1種類のEQP_IDがあるのですがEQP_IDが複数のファイルの中に入っている場合があり、EQP_IDが複数ある場合のみテキストファイルに書き出すというようにしたいです。
以前の質問では数万件あるファイルの中からキーとなる文字列を探してテキストファイルに書き出したのでテキストファイルの中身も数万件分と数が一致したのですが、今回はEQP_IDが複数ある場合のみテキストファイルに出力するので約1/4程度になる予定です。

現状のtxtファイル内 EQP_IDが複数あったり1つだけだったりで読み込んだ数万件のファイル全てをリスト化

EQP_ID LOT_ID WAFER_ID S_DATE RECIPE_ID
PCOT003 AP0077130.00C AP0077130.18 2019/4/3 23:48 PEPPR.084
WMPN002 AP0077148.00C AP0077148.19 2019/4/3 21:48 WET-MN01.084
PKRF008 AP0073456.00C AP0073456.18 2019/4/3 23:30 CMP-OXID.082
OFXS001 AP0012345.00C AP0012345.18 2019/4/3 20:48 PEPMA.084
PCOT004 AP0077131.00C AP0077131.18 2019/4/3 23:48 PEPPR.084
PCOT004 AP0077131.00C AP0077131.19 2019/4/3 23:49 PEPPR.084
AAAA001 AP0027130.00C AP0027130.18 2019/4/3 18:48 ABCDE.084
BBBB003 AP0037130.00C AP0037130.18 2019/4/3 19:48 FGHIJ.084
CCCC003 AP0047130.00C AP0047130.18 2019/4/3 12:48 KLMNO.084
DDDD003 AP0057130.00C AP0057130.18 2019/4/3 23:40 EEPPR.084
DDDD003 AP0057130.00C AP0057130.18 2019/4/3 23:41 EEPPR.084
PCOT006 AP0077154.00C AP0077154.18 2019/4/3 13:20 AIUEO.084

理想のtxtファイル内 EQP_IDが複数あるものだけをリスト化したので1/3~1/4程度のファイル数をリスト化

EQP_ID LOT_ID WAFER_ID S_DATE RECIPE_ID
PCOT003 AP0077130.00C AP0077130.18 2019/4/3 23:48 PEPPR.084
PCOT003 AP0077130.00C AP0077130.19 2019/4/3 23:49 PEPPR.084
PCOT003 AP0077130.00C AP0077130.20 2019/4/3 23:50 PEPPR.084
PCOT004 AP0077131.00C AP0077131.18 2019/4/3 23:48 PEPPR.084
PCOT004 AP0077131.00C AP0077131.19 2019/4/3 23:49 PEPPR.084
WMPN002 AP0077148.00C AP0077148.19 2019/4/3 21:48 WET-MN01.084
WMPN002 AP0077148.00C AP0077148.20 2019/4/3 21:49 WET-MN01.084
PKRF008 AP0073456.00C AP0073456.18 2019/4/3 23:30 CMP-OXID.082
PKRF008 AP0073456.00C AP0073456.19 2019/4/3 23:31 CMP-OXID.082
DDDD003 AP0057130.00C AP0057130.18 2019/4/3 23:40 EEPPR.084
DDDD003 AP0057130.00C AP0057130.18 2019/4/3 23:41 EEPPR.084

のように複数個の頭4文字が同じ(例:PCOT)EQP_IDがあるものだけをテキストファイルに出力して1個しかないEQP_IDについては出力しないようにしたいと考えています。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace ConsoleApp6
{
    class DatRowValues
    {
        public string ProcessData { get; set; }

        public string KeyValue { get; set; }

        public  string IntValue { get; set; }

        public string StringValue { get; set; }

        public string Value { get; set; }
    }
    class NewDatRowValues
    {
        public string EqpId { get; set; }

        public string LotId { get; set; }

        public string WaferId { get; set; }

        public DateTime SDate { get; set; }

        public string  RecipeId { 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");
            Map(x => x.RecipeId).Index(4);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            // 書き出し用の入れ物を用意
            var writeDatList = new List<NewDatRowValues>();

            // 読み込み
            foreach (string fileName in Directory.GetFiles(@"C:\20190404", "*.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" ||
                    r.KeyValue == "RECIPE_ID");

                    // それぞれの値を格納
                    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);
                        }
                        if (row.KeyValue == "RECIPE_ID")
                        {
                            newRow.RecipeId = row.StringValue;
                        }
                    }
                    writeDatList.Add(newRow);
                }
                    // 書き出し
                    using (var sw = new StreamWriter(@"C:\テスト\list.csv"))
                    using (var outputDat = new CsvHelper.CsvWriter(sw))
                    {
                        outputDat.Configuration.HasHeaderRecord = false;
                        outputDat.Configuration.RegisterClassMap<NewDatRowValuesMapper>();
                        outputDat.WriteRecords(writeDatList);
                    }
        }
    }
}

現状の私の知識と経験では実現出来ない仕様です。。。。

どうかご教授を宜しくお願い致します。

説明で分かりにくい部分や不足している部分がある場合はご指摘をお願い致します。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+2

LINQを使うと簡単です。
writeDatList が満たされているとすると

var writingList = writeDatList.GroupBy(r => r.EqpId)
    .Where(g => g.Count() > 1)
    .SelectMany(g => g)
    .ToList();


かな?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/12 09:47

    LINQの使い方についてもう少し詳しく解説をお願いできますでしょうか?
    サイトを見てもあまり理解できてなくて・・・

    var writingList = writeDatList.GroupBy(r => r.EqpId)
    //EqpIdから必要なキーを抽出
    .Where(g => g.Count() > 1)
    //EQP_IDが1より多いものを探す
    .SelectMany(g => g)
    //EQP_IDが複数個のもののみ選択
    .ToList();
    //リストに書き込む

    みたいな解釈でよろしいでしょうか?
    SelectManyの部分がSQLっぽい感覚だったのですね

    これを使うとしたら
    class Program
    {
    static void Main(string[] args)
    {
    // 書き出し用の入れ物を用意
    var writeDatList = new List<NewDatRowValues>();

    // 読み込み
    foreach (string fileName in Directory.GetFiles(@"C:\20190404", "*.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" ||
    r.KeyValue == "RECIPE_ID");

    // それぞれの値を格納
    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);
    }
    if (row.KeyValue == "RECIPE_ID")
    {
    newRow.RecipeId = row.StringValue;
    }
    }
    writeDatList.Add(newRow);
    }
    // 書き出し
    using (var sw = new StreamWriter(@"C:\テスト\list.csv"))
    using (var outputDat = new CsvHelper.CsvWriter(sw))
    {
    outputDat.Configuration.HasHeaderRecord = false;
    outputDat.Configuration.RegisterClassMap<NewDatRowValuesMapper>();
    outputDat.WriteRecords(writeDatList);
    }
    }
    }
    の一番最後の書き出しの部分を変更だと思うのですが
    間違えていたらすみません。

    キャンセル

  • 2019/04/12 11:15

    GroupByメソッド,SelectManyメソッドの解釈があやふやなところ以外はあってます。
    GroupByメソッド,SelectManyメソッドについて詳しくはそれぞれ
    "LINQ GroupBy","LINQ SelectMany"でweb検索をして調べてください。

    キャンセル

  • 2019/04/12 11:38

    https://www.sejuku.net/blog/47220
    https://www.sejuku.net/blog/56519
    https://qiita.com/nskydiving/items/c9c47c1e48ea365f8995

    色々な使い方があるのですね短期間で無理やり知識を詰め込んでる状態なので
    まだまだ未熟なところだらけです・・・

    詳しい解説 ありがとうございました!!

    キャンセル

0

回答ではありません。

確か、以前の質問というのが

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    LOT_ID    WAFER_ID    S_DATE    RECIPE_ID
という1行のデータにして別のCSVへ出力するというものだったと思います。
これは、1ファイル1組という制約のもとにプログラムされていたかと思います。

今回は、この元のファイル1つにEQP_IDなどの組み合わせが複数存在するファイルがあり、
そのファイルに出てきたEQP_IDなどの組み合わせのみ別ファイルに出力するということでしょうか?
例)

ProcessData,LOT_ID,3,AP0076686.00C,
ProcessData,LOT_NO,3,AP0076686,
ProcessData,WAFER_ID,3,AP0076686.19,
ProcessData,WAFER_NO,1,19,
ProcessData,EQP_ID,3,PKRF004,
ProcessData,LC_RECIPE_ID,3,PKRF.01,
ProcessData,RECIPE_ID,3,PEPMA.PES5MIX,
ProcessData,S_DATE,4,2019/01/24 12:47:09,
ProcessData,SLOT_NO,1,19,


というファイルのEQP_IDなどの組み合わせが1つのデータは出力せず

ProcessData,LOT_ID,3,AP0076686.00C,
ProcessData,LOT_NO,3,AP0076686,
ProcessData,WAFER_ID,3,AP0076686.19,
ProcessData,WAFER_NO,1,19,
ProcessData,EQP_ID,3,PKRF004,
ProcessData,LC_RECIPE_ID,3,PKRF.01,
ProcessData,RECIPE_ID,3,PEPMA.PES5MIX,
ProcessData,S_DATE,4,2019/01/24 12:47:09,
ProcessData,SLOT_NO,1,19,
ProcessData,LOT_ID,3,AP0011111.00B,
ProcessData,LOT_NO,3,AP0011111,
ProcessData,WAFER_ID,3,AP0011111.19,
ProcessData,WAFER_NO,1,19,
ProcessData,EQP_ID,3,PKRF003,
ProcessData,LC_RECIPE_ID,3,PKRF.01,
ProcessData,RECIPE_ID,3,PEPMA.PES5MIX,
ProcessData,S_DATE,4,2019/01/24 12:47:09,
ProcessData,SLOT_NO,1,19,


みたいに2組以上存在するファイルのデータを出力ということでしょうか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/12 10:39

    全部のファイルを処理した後の書き出しの部分だと思うのですが
    var writingList = writeDatList.GroupBy(r => r.EqpId)
    .Where(g => g.Count() > 1)
    .SelectMany(g => g)
    .ToList();
    の.ToList();を.ToList(@"C:\テスト\list.csv");
    というふうにしたら「引数1を指定するメソッドToListのオーバーロードはありません」というエラーが出てしまいます。

    // 書き出し
    using (var sw = new StreamWriter(@"C:\テスト\list.csv"))
    using (var outputDat = new CsvHelper.CsvWriter(sw))
    {
    outputDat.Configuration.HasHeaderRecord = false;
    outputDat.Configuration.RegisterClassMap<NewDatRowValuesMapper>();
    outputDat.WriteRecords(writeDatList);
    var writingList = writeDatList.GroupBy(r => r.EqpId)
    .Where(g => g.Count() > 1)
    .SelectMany(g => g)
    .ToList();
    }
    だとエラーはありませんが追加する前と後のファイルの数が変わりません・・・
    申し訳ないですが解説をお願い致します。

    キャンセル

  • 2019/04/12 11:09

    上記ソースですと
    outputDat.WriteRecords(writeDatList);
    の書き込みの後に
    var writingList = writeDatList.GroupBy(r => r.EqpId)~
    とリストの再構築をおこなっています。
    var writingList = writeDatList.GroupBy(r => r.EqpId)~
    とリストの再構築を行ってから
    // 書き出し
    にてwriteDatListではなくwritingList の書き出しを行うようにしてください。

    また、提示されたGroupbyだとEqpIdが完全一致するものをグループ化しますので、r.EqpId.SubString(0,4)など、左から4文字限定としてください。

    キャンセル

  • 2019/04/12 11:41

    なるほど!!
    意味が分かりました。あと上記のソースだとWriteRecords(writeDatList);をWriteRecords(writingList);
    に変えても宣言前には使用できないから

    // 書き出し
    using (var sw = new StreamWriter(@"C:\テスト\list.csv"))
    using (var outputDat = new CsvHelper.CsvWriter(sw))
    {
    var writingList = writeDatList.GroupBy(r => r.EqpId.Substring(0, 4))
    .Where(g => g.Count() > 1)
    .SelectMany(g => g)
    .ToList();
    outputDat.Configuration.HasHeaderRecord = false;
    outputDat.Configuration.RegisterClassMap<NewDatRowValuesMapper>();
    outputDat.WriteRecords(writingList);
    }
    ということですね。
    理想通りの結果を出すことができました。

    いつもありがとうございます。
    また宜しくお願い致します。

    キャンセル

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

  • ただいまの回答率 90.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • C#に関する質問
  • 数万件あるファイルの中身を読み込みある特定の文字列が同じものだけをテキストファイルに書き出す