2

csv 文字列の配列を検索し、データの特定のビットを 1 行に書き込む簡単な方法はありますか。現在、csv ファイルを検索し、TextBox からの 2 つのユーザー入力に基づいて特定のデータを引き出し、これを並べ替えて別の csv ファイルに書き込みます。

本来はデバイス名が同じなら同じ行にデータを書き込んでほしい。デバイスが「dev1 id1」と呼ばれ、別のデバイスが「dev1 id2」と呼ばれるとしましょう。(現在のように)両方を別々の行に書くのではなく、1つの結合された行に書きたいと思います。何かのようなもの:

dev1,id1,id2

それ以外の:

dev1,id1

dev1,id2

for ループと if ステートメントを使用してみましたが、すぐに面倒です。ベローは私の現在のコードです(仕事上の理由でタイプミスがあった場合は申し訳ありませんが、再入力する必要がありました)。

StreamWriter sw = new StreamWriter(@"c:\test.csv");
StreamReader sr = new StreamReader(@"c:\rtest.csv");

List<string> list = new List<string>();
string line = "Station,Device,Key,AOR";
string sta = textBox1.Text;
string[] devs = richTextBox1.Text.Split(',').Select(dev => dev.Trim()).ToArray();
string[] sort,strs;
bool cont;

sw.WriteLine(line);

while (!sr.EndOfStream)
{
    strs = line.Split(',');
    cont = (devs.Any(s => strs[1].IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0));
    if (strs[2].ToString() == sta && cont ==true)
    {
        list.Add(line.ToString());
    }
    line = sr.ReadLine();
}
sort = new string[list.Count];
list.CopyTo(sort);
Array.Sort(sort);

foreach (string var in sort)
{
    strs = var.Split(',');
    sw.WriteLine(string.Format("{2},{1},{0},{3}", strs[0], strs[1], strs[2], strs[3]));
}

sw.Close();
if (File.Exists(@"c:\test.csv")
{
    Process.Start(@"c:\test.csv");
}

私の質問が理解できることを願っています、ありがとう。

4

3 に答える 3

1

CSV ファイルを読み取るには、サードパーティのパーサーを使用する必要があります。負担が軽減されます。

たとえば、KBCsvを使用すると、コードは次のようになります (元は VB.NET で記述されているため、変換エラーについては申し訳ありません)。

Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();
using (CsvReader reader = new CsvReader("Input.csv")) {
  foreach (string[] record in reader.DataRecordsAsStrings) {
    //assuming each record contains exactly 2 columns, under index 0 and 1
    string key = record[0];
    string value = record[1];
    List<string> targetList = null;
    if (!dict.TryGetValue(key, out targetList))
    {
      targetList = new List<string>();
      dict.Add(key, targetList);
    }
    targetList.Add(value);
  }
}
List<string> output = new List<string>();
foreach (KeyValuePair<string, List<string>> kv in dict)
{
  string outputCsvLine = kv.Key + "," + string.Join(",", kv.Value);
  output.Add(outputCsvLine);
}
System.IO.File.WriteAllLines("output.csv", output);

出力を書き込むために、カンマや引用符などの CSV 特殊文字が値内にある場合は、それらをエスケープすることを検討する必要がある場合があります。

編集: (セキュリティまたはその他の理由で) サードパーティのコンポーネントを本当に避けたい場合は、上記のコードの 2 行目と 3 行目を次のように変更できます。

using (System.IO.StreamReader reader = new System.IO.StreamReader("Input.csv")) {
  foreach (string rawRecord in reader.ReadLine()) {        
    string[] record = rawRecord.Split(",");

免責事項:そのようなCSVファイルを扱うのは悪い習慣ですが、あなたの特定の例ではうまくいきます. コードの残りの部分は同じままです。

于 2012-11-27T02:19:14.017 に答える
1

Linq を使用してコードをクリーンアップします。

  • キーごとに一連の値を取得するための GroupBy
  • 値を再び結合する場合に選択します。

必要なのは、IEnumerable を実装するものだけです。「、」で分割する非常に基本的なバージョンを含めました。

keySelector ラムダはキーを抽出します
valueSelector ラムダは値を抽出します

    [TestMethod]
    public void CsvParser()
    {
        string columns = "Device,Id";
        string source = "Dev1,id1" + Environment.NewLine + "Dev1,id2" + Environment.NewLine + "Dev2,id3" + Environment.NewLine + "Dev2,id4";

        List<string> columnNames = columns.Split(',').ToList();

        int keyIndex = columnNames.IndexOf("Device");
        int valueIndex = columnNames.IndexOf("Id");

        GroupByKey(keyIndex, valueIndex, source);
    }

    private void GroupByKey(int keyIndex, int valueIndex, string source)
    {
        LineReader reader = new LineReader(new StringReader(source));

        Func<string[], string> keySelector = lineItems => lineItems[keyIndex];
        Func<string[], string> valueSelector = lineItems => lineItems[valueIndex];

        List<string> idsByDev = reader
            // .Skip(1)  <-- Uncomment if first row contains headers
            .GroupBy(keySelector, valueSelector, StringComparer.OrdinalIgnoreCase)
            .Select(device => device.Key + "," + string.Join(",", device))
            .ToList()
            ;

        Console.WriteLine( string.Join(Environment.NewLine, idsByDev ));
    }

    public class LineReader : IEnumerable<string[]>
    {
        private readonly TextReader source;

        public LineReader( TextReader source )
        {
            this.source = source;
        }

        public IEnumerator<string[]> GetEnumerator()
        {
            return new LineReaderEnumerator(this.source);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        private class LineReaderEnumerator : IEnumerator<string[]>
        {
            private TextReader source;

            public LineReaderEnumerator(TextReader source)
            {
                this.source = source;
            }

            public void Dispose()
            {
                this.source.Dispose();
            }

            public bool MoveNext()
            {
                // Replace these lines with a good CSV parser
                string line = source.ReadLine();

                if (!String.IsNullOrEmpty(line))
                {
                    this.Current = line.Split(',');
                }
                else
                {
                    this.Current = null;
                }

                return this.Current != null;
            }

            public void Reset()
            {
                throw new NotImplementedException();
            }

            public string[] Current { get; private set; }

            object IEnumerator.Current
            {
                get { return Current; }
            }
        }
    }
于 2012-11-27T04:05:39.673 に答える
1

コンマを使用して単純に CSV 行を分割するのは、信頼できる方法ではありません。フィールドにはコンマや改行を含めることができます。

指摘したように、CSV ファイルを適切に読み取ることができる .NET のライブラリがあります。または、記事Read and Writing CSV Files in C# で紹介したコードを使用することもできます。

于 2012-11-27T02:39:29.433 に答える