回答編集履歴
3
DataGridView
    
        answer	
    CHANGED
    
    | @@ -61,4 +61,172 @@ | |
| 61 61 | 
             
                    }
         | 
| 62 62 | 
             
                }
         | 
| 63 63 | 
             
            }
         | 
| 64 | 
            -
            ```
         | 
| 64 | 
            +
            ```
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            ---
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            `DataGridView`がマストとなると、`Datatable`を選択すべきでしょう。
         | 
| 69 | 
            +
            当初の回答とは方向性が変わりましたが、やること自体は同じような感じです(`Dictionary`の代わりに`Datatable`になっただけ)
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            どうせなのでより実践的?(エンコードやまともな`Split`)にしました。
         | 
| 72 | 
            +
            CsvHelperでもなんでもいいのですが、変則csvを扱いやすかったので↓を使用しました。
         | 
| 73 | 
            +
            [NuGet Gallery | Csv 2.0.84](https://www.nuget.org/packages/Csv/2.0.84)
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            出番がないのはかわいそうなので、`PropertyGrid`もついでに付けましたw
         | 
| 76 | 
            +
            `DataGridView`で複数行選択すると、`PropertyGrid`で値を一括変更できます(なぜか単一選択と複数選択で項目の並び順が変わる。なんで??)
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            ```cs
         | 
| 79 | 
            +
            using System;
         | 
| 80 | 
            +
            using System.Collections.Generic;
         | 
| 81 | 
            +
            using System.ComponentModel;
         | 
| 82 | 
            +
            using System.Data;
         | 
| 83 | 
            +
            using System.Diagnostics;
         | 
| 84 | 
            +
            using System.IO;
         | 
| 85 | 
            +
            using System.Linq;
         | 
| 86 | 
            +
            using System.Text;
         | 
| 87 | 
            +
            using System.Windows.Forms;
         | 
| 88 | 
            +
            using Csv;
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            namespace QQ8aid4d1edch6jx
         | 
| 91 | 
            +
            {
         | 
| 92 | 
            +
                public partial class Form1 : Form
         | 
| 93 | 
            +
                {
         | 
| 94 | 
            +
                    private readonly DataTable dataTable = new DataTable();
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    public Form1()
         | 
| 97 | 
            +
                    {
         | 
| 98 | 
            +
                        InitializeComponent();
         | 
| 99 | 
            +
                        openFileDialog1.Filter = "CSVファイル|*.csv";
         | 
| 100 | 
            +
                        dataGridView1.DataSource = dataTable;
         | 
| 101 | 
            +
                        dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
         | 
| 102 | 
            +
                        propertyGrid1.ToolbarVisible = false;
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                        File.WriteAllText("csv1.csv", "場所,東京,取引先,株式会社A,商品,食品", Encoding.GetEncoding("shift_jis"));
         | 
| 105 | 
            +
                        File.WriteAllText("csv2.csv", "実績,あり,商品,文具,取引先,株式会社B,場所,大阪,実店舗,あり", Encoding.GetEncoding("shift_jis"));
         | 
| 106 | 
            +
                    }
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    // 変則csvの読み込み
         | 
| 109 | 
            +
                    private void LoadButton_Click(object sender, EventArgs e)
         | 
| 110 | 
            +
                    {
         | 
| 111 | 
            +
                        if (openFileDialog1.ShowDialog() == DialogResult.OK)
         | 
| 112 | 
            +
                        {
         | 
| 113 | 
            +
                            DataRow row = dataTable.NewRow();
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                            // 値にカンマや改行が含まれることも考慮しちゃんとパースする
         | 
| 116 | 
            +
                            string csv = File.ReadAllText(openFileDialog1.FileName, Encoding.GetEncoding("shift_jis"));
         | 
| 117 | 
            +
                            var options = new CsvOptions { HeaderMode = HeaderMode.HeaderAbsent, AllowNewLineInEnclosedFieldValues = true, };
         | 
| 118 | 
            +
                            string[] split = CsvReader.ReadFromText(csv, options).First().Values;
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                            // カラム数が奇数の場合はどうすべきか?(とりあえずハブったものは無視)
         | 
| 121 | 
            +
                            for (var i = 0; i < split.Length - 1; i += 2)
         | 
| 122 | 
            +
                            {
         | 
| 123 | 
            +
                                string key = split[i];
         | 
| 124 | 
            +
                                string value = split[i + 1];
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                                if (!dataTable.Columns.Contains(key)) // まだ該当カラムがなければ...
         | 
| 127 | 
            +
                                {
         | 
| 128 | 
            +
                                    dataTable.Columns.Add(key); // カラム追加
         | 
| 129 | 
            +
                                }
         | 
| 130 | 
            +
                                row[key] = value;
         | 
| 131 | 
            +
                            }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                            dataTable.Rows.Add(row);
         | 
| 134 | 
            +
                        }
         | 
| 135 | 
            +
                    }
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    // DataTableをまともなcsvに出力
         | 
| 138 | 
            +
                    private void SaveButton_Click(object sender, EventArgs e)
         | 
| 139 | 
            +
                    {
         | 
| 140 | 
            +
                        var header = new List<string>(); // csvヘッダー部分
         | 
| 141 | 
            +
                        foreach (DataColumn column in dataTable.Columns)
         | 
| 142 | 
            +
                        {
         | 
| 143 | 
            +
                            header.Add(column.Caption); // カラムヘッダーを順番に追加
         | 
| 144 | 
            +
                        }
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                        var data = new List<string[]>(); // csvデータ部分(2次元配列のような状態)
         | 
| 147 | 
            +
                        foreach (DataRow row in dataTable.Rows)
         | 
| 148 | 
            +
                        {
         | 
| 149 | 
            +
                            var items = new List<string>(); // 1行分
         | 
| 150 | 
            +
                            foreach (object item in row.ItemArray)
         | 
| 151 | 
            +
                            {
         | 
| 152 | 
            +
                                items.Add(item.ToString()); // 1セル分
         | 
| 153 | 
            +
                            }
         | 
| 154 | 
            +
                            data.Add(items.ToArray());
         | 
| 155 | 
            +
                        }
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                        // ダブルクォート処理等いい感じにやってくれる
         | 
| 158 | 
            +
                        var csv = CsvWriter.WriteToText(header.ToArray(), data, ',');
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                        // 上記foreachをワンライナーで書くなら
         | 
| 161 | 
            +
                        //var header = dataTable.Columns.Cast<DataColumn>().Select(x => x.Caption).ToArray();
         | 
| 162 | 
            +
                        //var data = dataTable.Rows.Cast<DataRow>().Select(x => x.ItemArray.Select(y => y.ToString()).ToArray());
         | 
| 163 | 
            +
                        //var csv = CsvWriter.WriteToText(header, data, ',');
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                        File.WriteAllText("out.csv", csv, Encoding.GetEncoding("shift_jis"));
         | 
| 166 | 
            +
                        Process.Start("out.csv");
         | 
| 167 | 
            +
                    }
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                    private void DataGridView1_SelectionChanged(object sender, EventArgs e)
         | 
| 170 | 
            +
                    {
         | 
| 171 | 
            +
                        propertyGrid1.SelectedObjects = dataGridView1.SelectedRows
         | 
| 172 | 
            +
                            .Cast<DataGridViewRow>()
         | 
| 173 | 
            +
                            .Select(x => new RowWrapper((DataRowView)x.DataBoundItem))
         | 
| 174 | 
            +
                            .ToArray();
         | 
| 175 | 
            +
                    }
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                    private void PropertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
         | 
| 178 | 
            +
                    {
         | 
| 179 | 
            +
                        dataGridView1.Refresh();
         | 
| 180 | 
            +
                    }
         | 
| 181 | 
            +
                }
         | 
| 182 | 
            +
             | 
| 183 | 
            +
             | 
| 184 | 
            +
                // DataRowをいい感じにPropertyGridに表示する
         | 
| 185 | 
            +
                // [C#/winforms: how to best bind a propertygrid and a System.Data.DataRow - Stack Overflow](https://stackoverflow.com/questions/943621/c-winforms-how-to-best-bind-a-propertygrid-and-a-system-data-datarow)
         | 
| 186 | 
            +
                [TypeConverter(typeof(RowWrapperConverter))]
         | 
| 187 | 
            +
                class RowWrapper
         | 
| 188 | 
            +
                {
         | 
| 189 | 
            +
                    public List<string> Exclude { get; } = new List<string>();
         | 
| 190 | 
            +
                    private readonly DataRowView rowView;
         | 
| 191 | 
            +
                    public RowWrapper(DataRowView rowView) => this.rowView = rowView;
         | 
| 192 | 
            +
                    private static DataRowView GetRowView(object component) => ((RowWrapper)component).rowView;
         | 
| 193 | 
            +
                    private class RowWrapperConverter : TypeConverter
         | 
| 194 | 
            +
                    {
         | 
| 195 | 
            +
                        public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true;
         | 
| 196 | 
            +
                        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
         | 
| 197 | 
            +
                        {
         | 
| 198 | 
            +
                            var rw = (RowWrapper)value;
         | 
| 199 | 
            +
                            var props = TypeDescriptor.GetProperties(GetRowView(value), attributes);
         | 
| 200 | 
            +
                            var result = new List<PropertyDescriptor>(props.Count);
         | 
| 201 | 
            +
                            foreach (PropertyDescriptor prop in props)
         | 
| 202 | 
            +
                            {
         | 
| 203 | 
            +
                                if (rw.Exclude.Contains(prop.Name)) continue;
         | 
| 204 | 
            +
                                result.Add(new RowWrapperDescriptor(prop));
         | 
| 205 | 
            +
                            }
         | 
| 206 | 
            +
                            return new PropertyDescriptorCollection(result.ToArray());
         | 
| 207 | 
            +
                        }
         | 
| 208 | 
            +
                    }
         | 
| 209 | 
            +
                    private class RowWrapperDescriptor : PropertyDescriptor
         | 
| 210 | 
            +
                    {
         | 
| 211 | 
            +
                        private static Attribute[] GetAttribs(AttributeCollection value)
         | 
| 212 | 
            +
                        {
         | 
| 213 | 
            +
                            if (value == null) return null;
         | 
| 214 | 
            +
                            var result = new Attribute[value.Count];
         | 
| 215 | 
            +
                            value.CopyTo(result, 0);
         | 
| 216 | 
            +
                            return result;
         | 
| 217 | 
            +
                        }
         | 
| 218 | 
            +
                        private readonly PropertyDescriptor innerProp;
         | 
| 219 | 
            +
                        public RowWrapperDescriptor(PropertyDescriptor innerProperty) : base(innerProperty.Name, GetAttribs(innerProperty.Attributes)) => innerProp = innerProperty;
         | 
| 220 | 
            +
                        public override bool ShouldSerializeValue(object component) => innerProp.ShouldSerializeValue(GetRowView(component));
         | 
| 221 | 
            +
                        public override void ResetValue(object component) => innerProp.ResetValue(GetRowView(component));
         | 
| 222 | 
            +
                        public override bool CanResetValue(object component) => innerProp.CanResetValue(GetRowView(component));
         | 
| 223 | 
            +
                        public override void SetValue(object component, object value) => innerProp.SetValue(GetRowView(component), value);
         | 
| 224 | 
            +
                        public override object GetValue(object component) => innerProp.GetValue(GetRowView(component));
         | 
| 225 | 
            +
                        public override Type PropertyType => innerProp.PropertyType;
         | 
| 226 | 
            +
                        public override Type ComponentType => typeof(RowWrapper);
         | 
| 227 | 
            +
                        public override bool IsReadOnly => innerProp.IsReadOnly;
         | 
| 228 | 
            +
                    }
         | 
| 229 | 
            +
                }
         | 
| 230 | 
            +
            }
         | 
| 231 | 
            +
            ```
         | 
| 232 | 
            +
            
         | 
2
Single
    
        answer	
    CHANGED
    
    | @@ -34,7 +34,7 @@ | |
| 34 34 |  | 
| 35 35 |  | 
| 36 36 | 
             
                        var csv1 = new Dictionary<string, string>(); // 辞書(Dictionary)を作る
         | 
| 37 | 
            -
                        var line1 = File.ReadLines("csv1.csv").Single(); // 1行読み込む
         | 
| 37 | 
            +
                        var line1 = File.ReadLines("csv1.csv").Single(); // 1行読み込む(1行でなかったらエラーFirstのほうがいいかも?)
         | 
| 38 38 | 
             
                        var split1 = line1.Split(new char[] { ',' }); // カンマで区切る
         | 
| 39 39 | 
             
                        for (var i = 0; i < split1.Length; i += 2) // 2個セットで使うので+=2
         | 
| 40 40 | 
             
                        {
         | 
1
C# 7.3
    
        answer	
    CHANGED
    
    | @@ -16,34 +16,49 @@ | |
| 16 16 | 
             
            どうしてもプロパティっぽく書きたいのであれば`ExpandoObject`が使えますが、便利かどうかはわかりません。
         | 
| 17 17 | 
             
            [ExpandoObject クラス (System.Dynamic) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.dynamic.expandoobject)
         | 
| 18 18 |  | 
| 19 | 
            -
            .NET6です^^
         | 
| 20 19 | 
             
            ```cs
         | 
| 20 | 
            +
            using System;
         | 
| 21 | 
            +
            using System.Collections.Generic;
         | 
| 21 22 | 
             
            using System.Dynamic;
         | 
| 23 | 
            +
            using System.IO;
         | 
| 24 | 
            +
            using System.Linq;
         | 
| 22 25 |  | 
| 26 | 
            +
            namespace Q8aid4d1edch6jx
         | 
| 27 | 
            +
            {
         | 
| 28 | 
            +
                class Program
         | 
| 29 | 
            +
                {
         | 
| 30 | 
            +
                    static void Main()
         | 
| 31 | 
            +
                    {
         | 
| 23 | 
            -
            File.WriteAllText("csv1.csv", "場所,東京,取引先,株式会社A,商品,食品");
         | 
| 32 | 
            +
                        File.WriteAllText("csv1.csv", "場所,東京,取引先,株式会社A,商品,食品");
         | 
| 24 | 
            -
            File.WriteAllText("csv2.csv", "実績,あり,商品,食品,取引先,株式会社A,場所,東京,実店舗,あり");
         | 
| 33 | 
            +
                        File.WriteAllText("csv2.csv", "実績,あり,商品,食品,取引先,株式会社A,場所,東京,実店舗,あり");
         | 
| 25 34 |  | 
| 26 35 |  | 
| 36 | 
            +
                        var csv1 = new Dictionary<string, string>(); // 辞書(Dictionary)を作る
         | 
| 27 | 
            -
            var  | 
| 37 | 
            +
                        var line1 = File.ReadLines("csv1.csv").Single(); // 1行読み込む
         | 
| 28 | 
            -
             | 
| 38 | 
            +
                        var split1 = line1.Split(new char[] { ',' }); // カンマで区切る
         | 
| 39 | 
            +
                        for (var i = 0; i < split1.Length; i += 2) // 2個セットで使うので+=2
         | 
| 40 | 
            +
                        {
         | 
| 29 | 
            -
             | 
| 41 | 
            +
                            csv1.Add(split1[i], split1[i + 1]); // 2個セットの前をキー後ろを値に
         | 
| 30 | 
            -
             | 
| 42 | 
            +
                        }
         | 
| 31 | 
            -
                           .ToDictionary(x => x[0], x => x[1]);
         | 
| 32 43 |  | 
| 33 | 
            -
            Console.WriteLine(csv1["場所"]); // 東京
         | 
| 44 | 
            +
                        Console.WriteLine(csv1["場所"]); // 東京  キーから値を取得(キーがないとKeyNotFoundException)
         | 
| 34 | 
            -
            Console.WriteLine(csv1. | 
| 45 | 
            +
                        Console.WriteLine(csv1.TryGetValue("実績", out var value) ? value : "項目なし"); // 項目なし  キーがあるかどうかわからない場合はTryGetValue
         | 
| 35 46 |  | 
| 36 47 |  | 
| 37 | 
            -
            dynamic csv2 = new ExpandoObject();
         | 
| 48 | 
            +
                        dynamic csv2 = new ExpandoObject();
         | 
| 38 49 |  | 
| 39 | 
            -
            var  | 
| 50 | 
            +
                        var line2 = File.ReadLines("csv2.csv").Single();
         | 
| 40 | 
            -
            var  | 
| 51 | 
            +
                        var split2 = line2.Split(new char[] { ',' });
         | 
| 41 | 
            -
            for (var i = 0; i <  | 
| 52 | 
            +
                        for (var i = 0; i < split2.Length; i += 2)
         | 
| 42 | 
            -
            {
         | 
| 53 | 
            +
                        {
         | 
| 43 | 
            -
             | 
| 54 | 
            +
                            var dic = (IDictionary<string, object>)csv2; // ExpandoObjectはIDictionaryにキャストできる
         | 
| 55 | 
            +
                            dic.Add(split2[i], split2[i + 1]); // Dictionaryと同じようにAddするとプロパティが生えてくるように見える
         | 
| 56 | 
            +
                        }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                        Console.WriteLine(csv2.場所); // 東京  プロパティのようにアクセスできるが、インテリセンスが効くわけでもないので便利かどうかはわからない
         | 
| 59 | 
            +
                        Console.WriteLine(csv2.実績); // あり
         | 
| 60 | 
            +
                        //Console.WriteLine(csv2.ほげ); // RuntimeBinderException
         | 
| 61 | 
            +
                    }
         | 
| 62 | 
            +
                }
         | 
| 44 63 | 
             
            }
         | 
| 45 | 
            -
             | 
| 46 | 
            -
            Console.WriteLine(csv2.場所); // 東京
         | 
| 47 | 
            -
            Console.WriteLine(csv2.実績); // あり
         | 
| 48 | 
            -
            //Console.WriteLine(csv2.ほげ); // RuntimeBinderException
         | 
| 49 64 | 
             
            ```
         | 
