How to group text file according to parameters of the first line in C #

0

I was able to merge several text files from the same directory into a final text file, grouping the same codes and summing their respective amounts, using the following code (credits to friend Vitor Mendes):

Dictionary<string, int> valores = new Dictionary<string, int>();

string diretorio = @"C:\teste";

string[] listaDeArquivos = Directory.GetFiles(diretorio);

if (listaDeArquivos.Length > 0)
{
    string caminhoArquivoDestino = @"C:\teste\saida.txt";

    FileStream arquivoDestino = File.Open(caminhoArquivoDestino, FileMode.OpenOrCreate);

    arquivoDestino.Close();

    List<string> linhasDestino = new List<string>();

    foreach (string caminhoArquivo in listaDeArquivos)
    {
         foreach (var linhaArquivoAtual in File.ReadAllLines(caminhoArquivo))
         {
            string id = linhaArquivoAtual.Substring(0, linhaArquivoAtual.Length - 3);
            string quantidade = linhaArquivoAtual.Substring(linhaArquivoAtual.Length - 3, 3);

            if (valores.ContainsKey(id)) 
            valores[id] = valores[id] + Convert.ToInt32(quantidade);
            else
            valores.Add(id, Convert.ToInt32(quantidade));           

         }
    }

    File.WriteAllLines(caminhoArquivoDestino, valores.Select(x => x.Key + x.Valeu.ToString("000")).ToArray());
}

The first line of the home text file contains 2 point-separated identification parameters. I'll illustrate:

Conteúdo do Arq1.txt
000032;30032014
123456010
654321020

Conteúdo do Arq2.txt
000032;30032014
123456005
654321005

Conteúdo do Arq3.txt
000033;23052014
123456050
654321020

Conteúdo do Arq4.txt
000033;23052014
123456020
654321005

Conteúdo do Arq5.txt
000033;20052014
123456001
654321002

Conteúdo do Arq6.txt
000033;20052014
123456009
654321008

When grouping these files, the program should generate different final files according to the parameters of the first line. In these sample files, the end result will be the following files:

ArqFinal00003320052014.txt
123456010
654321010

ArqFinal00003323052014.txt
123456070
654321025

ArqFinal00003230032014.txt
123456015
654321025

That is, the program should group the files according to the first line, creating different final files.

    
asked by anonymous 23.05.2014 / 14:12

2 answers

5

As example in this answer , a dictionary is the solution for grouping items. In case you now have two levels of grouping, then the use of nested dictionaries is necessary. You have the dictionary with the names of the files as a key and the value is another dictionary with codes as key and the amounts as value.

The code is far from great but it is tested and does what you want.

The comments have been placed for didactic purposes and does not express the way I comment codes.

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

public class MergeFiles {
    public static void Main(string[] args) {
        var itens = new Dictionary<string, Dictionary<string, int>>(); //Cria a estrutura que permite chaves únicas do tipo string e valores associados do tipo int
        int resultado;
        foreach (var arquivo in Directory.GetFiles(@"C:\teste", "*.txt")) { //Pega todos os arquivos com extensão txt disponíveis no diretório
            var chaveArquivo = "";
            foreach (var linha in File.ReadAllLines(arquivo)){ //Lê todas as linhas individualmente de cada arquivo
                if (linha.Substring(6, 1)  == ";") { //Verifica se esta é a primeira linha
                   chaveArquivo = linha.Substring(0, 6) + linha.Substring(7, 8); //Pega os 6 primeiros caracteres e os 8 sequintes pulando o ;
                    if (!itens.ContainsKey(chaveArquivo)) { //verifica se não existe a chave com nome do arquivo
                        itens.Add(chaveArquivo, new Dictionary<string, int>()); //Adiciona uma nova chave ainda inexistente no dicionário
                    }
                } else {
                    var chave = linha.Substring(0, 6); //Pega os 6 primeiros caracteres
                    var valor = (int.TryParse(linha.Substring(6, 3), out resultado) ? resultado : 0); //Pega os 3 caracteres seguintes e converte para numérico
                    if (itens[chaveArquivo].ContainsKey(chave)) { //verifica se já existe a chave no dicionário
                        itens[chaveArquivo][chave] = itens[chaveArquivo][chave] + valor; //adiciona o valor obtido na linha à chave já existe no dicionário
                    } else {
                        itens[chaveArquivo].Add(chave, valor); //Adiciona uma nova chave ainda inexistente no dicionário
                    }
                }
            }
        }
        //Cria os arquivos agrupados adicionando todas as linhas do dicionário recriando a mesma estrutura anterior através do LINQ
        foreach(var arquivo in itens) {
            File.WriteAllLines(arquivo.Key + ".txt", arquivo.Value.Select(item => item.Key + item.Value.ToString("000")).ToArray());
        }
    }
}

I placed it on GitHub for future reference.

    
23.05.2014 / 18:58
2

Using the question code, it should look like this:

static void Main(string[] args)
{
    string diretorio = @"C:\teste";

    String[] listaDeArquivos = Directory.GetFiles(diretorio);

    if (listaDeArquivos.Length > 0)
    {

        FileStream arquivoDestino = File.Open(caminhoArquivoDestino, FileMode.OpenOrCreate);
        arquivoDestino.Close();

        String[] linhasDestino;

        Dictionary<String, List<String>> out = new Dictionary<String, List<String>>();

        foreach (String caminhoArquivo in listaDeArquivos)
        {
            linhasDestino = File.ReadAllLines(caminhoArquivo);
            String name = linhasDestino[0].replace(';','');
            if (!out.ContainsKey(name))
            {
                out[name] = new List<String>();
            }
            for(int i = 1; i < linhasDestino.Length; i++)
            {
                out[name].Add(linhasDestino[i]);
            }
        }

        foreach(String key in out.Keys)
        {
            string caminhoArquivoDestino = @"C:\teste\"+key;
            File.WriteAllLines(caminhoArquivoDestino, out[key].ToArray());
        }
    }
}

I have no compiler here, but this should work. What is done is as follows:

  • Open the file;
  • For each open file, it sees if an entry already exists with that name of the first line of the file;
  • Adds all rows (from the second) to a list associated with that name;
  • Then write all lines in files with associated names.
  • Basically, the idea of the algorithm is this.

        
    23.05.2014 / 16:24