How to group code and add existing amounts in text files with C#?

Asked

Viewed 1,315 times

2

I have several text files inside a directory. I need to do in C# and join all the text files in a final text file, but treating their contents.

Example:

Arq1.txt
123456010
654321020

Arq2.txt
123456040
654321005

The first 6 characters are a code and the final 3 characters are the quantity. I need to join the files in some way that is generated a final file contemplating the codes, without repeating them and adding the quantities.

ArqFinal.txt
123456050
654321025

Sorry I don’t code, but I really have no idea.

  • You know regular expressions?

  • Hello Gypsy. I don’t know, but because you mentioned I started researching. I can already separate and identify the fields, in theory.

  • If all numbers are aligned, just use substring splitting, in a very simple way and without the need for regex.

  • @Bacco yes, they are "aligned", that is, the number of characters in the code field will always be 6 (the first six) and the number of characters in the code field will always be 3 (the last three). I’ll search the division.

  • @Vhox is really a case for substring. You can store the 6-digit ID as a key in an array, and add up the values in this array. I’m just not gonna venture into writing the code because I don’t use C#.

  • @DBX8 without knowing the language details, I would extract ID and QTD, and do something like this: if index ID does not exist in the array, it creates the ID input with QTD value, otherwise it adds QTD to the value of the existing ID. after reading all txts, it would generate the output. This approach would only be problematic if the txts were gigantic.

  • I thank you all. This is very difficult for me, but I will keep trying. If you have suggestions and help, you are more than welcome. Abs!

Show 2 more comments

4 answers

6

I adapted to previous answer for this case. I haven’t thought of all the possibilities. You have not given criteria to solve file malformation problems, if there is how to validate the codes and what happens if the quantity does not contain a valid numerical value.

I considered it implicit that the values of the quantity are always integers and that an invalid value would be considered zero. I also considered that the minimum a line should have of validation is the exact size of 9 characters.

I used an auxiliary data structure to put in memory all the codes through unique keys and adding the quantities in the existing codes.

I did a quick test and is presenting the expected result. The code can certainly be better organized.

Comments are being used only for didactic purposes and do not reflect my coding style.

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

public class MergeFiles {
    public static void Main(string[] args) {
        var caminhoOrigem = @"C:\teste";
        var nomeArquivoCompleto = @"C:\teste\saida.txt";
        var itens = new 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(caminhoOrigem, "*.txt")) { //Pega todos os arquivos com extensão txt disponíveis no diretório
            if (arquivo != nomeArquivoCompleto) { //Não deixa processar o próprio arquivo que está sendo criado
                foreach (var linha in File.ReadAllLines(arquivo)) { //Lê todas as linhas individualmente de cada arquivo
                    if (linha.Length == 9) { //Garante que a linha tem 9 caracteres
                        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.ContainsKey(chave)) { //verifica se já existe a chave no dicionário
                            itens[chave] = itens[chave] + valor; //adiciona o valor obtido na linha à chave já existe no dicionário
                        } else {
                            itens.Add(chave, valor); //Adiciona uma nova chave ainda inexistente no dicionário
                        }
                    }
                }
            }
        }
        //Cria o arquivo destino adicionando todas as linhas do dicionário recriando a mesma estrutura anterior através do LINQ
        File.WriteAllLines(nomeArquivoCompleto, itens.Select(item => item.Key + item.Value.ToString("000")).ToArray());
    }
}

I put in the Github for future reference.

2

Surely this thread is continuation of this (How to merge multiple text files into one? ), then I will use the code posted by @reiksiel to complement the example:

Well, as you said yourself, the quantity is represented by the last 3 digits, so we can use a Dictionary where this ID would be the key and if there is already this ID in the dictionary you will add to then export to the . txt, something like this:

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());
}

I could not test this code above because I am without Visual Studio, but it would be something like this.

  • Hello Vitor. Thank you for your attention. I will test!

  • Vitor, if possible you can enlighten me to what you can refer if nakele with "values"? q values? Sorry amateurism. Abs.

  • Of course @Vhox, values is a variable for that dictionary where the main key is a string and the value of the key is an integer, it is checked if there is already a key in this dictionary (123456), if it does not exist it is added in the dictionary, however if it exists, I recover the value and add the new amount with the previous.

  • Man, that’s cool logic. I tried the tests here and gave the following error: 'int' does not contain a Definition for 'Value' and no Extension method 'Value' Accepting a first argument of type 'int' could be found (are you Missing a using Directive or an Assembly Reference?)

  • @Vhox fixed the code, the error was on this line ( values[id] = values[id]. Value + Convert.Toint32(quantity)), the property . Valeu does not exist when accessing directly by the dictionary idnex, just remove . Value

  • Vitor, thanks again! The code was excellent.

Show 1 more comment

2

string[] arrayFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.txt");
string outputFile = Directory.GetCurrentDirectory() + @"\ArquivoSaida.txt";
Dictionary<string, int> values = new Dictionary<string, int>();

   if (File.Exists(outputFile))
   {
      Console.WriteLine("O arquivo de saída existe!");
      Console.ReadLine();
      Environment.Exit(0);
   }

   for (int i = 0; i < arrayFiles.Count(); i++)
   {
      foreach (string line in File.ReadAllLines(arrayFiles[i]))
      {
         string id  = Regex.Match(line, @"[0-9]...{3}").Value;
         string qtd = Regex.Match(line, @"[0-9]..\z{3}").Value;

      if (values.ContainsKey(id))
         values[id] = values[id] + Convert.ToInt32(qtd);
      else
         values.Add(id, Convert.ToInt32(qtd));  
       }
    }
    File.WriteAllLines(outputFile , values.Select(x => x.Key + x.Value).ToArray());
    Console.WriteLine("Procedimento realizado, Pressione alguma tecla para sair...");
    Console.ReadLine();

This will search for all text files in the current working directory, if the output file exists, the program will send a message saying that the file exists and will terminate the program. Different from other great answers where the method is used String.Substring, the use of regular expressions, simple but functional expressions in the test I performed.

txtfile1.txt
   123456010
   654321020
txtfile2.txt
   123456040
   654321005
txtfile3.txt
   123456080
   654321007
ArquivoSaida.txt
   123456130
   65432132

Graças @Bacco !

1

Using Filehelpers this looks like it won’t be too complicated.

Class of the layout of records containing the code and the quantity:

public class Registro
{
    [FieldFixedLength(6)]
    public int Codigo {get; set; }

    [FieldFixedLength(3)]
    public int Quantidade {get; set; }
}

using FileHelpers;

// Realiza todo o processamento. Observe os métodos abaixo para entender o código.
public void CombinarRegistros(List<string> listaDeArquivos)
{
    var listaDeRegistros = new List<Registro>();

    foreach (var arquivo in listaDeArquivos)
    {
        listaDeArquivos.AddRange(ObtenhaRegistros(arquivo));
    }

    var registrosCombinados = CombineQuantidades(listaDeRegistros);

    EscrevaArquivoDosRegistrosCombinados("ArquivoFinal.txt", registrosCombinados);
}

// Lê os registros dos arquivos originais.
public List<Registro>() ObtenhaRegistros(string nomeDoArquivo)
{
    var engine = new MultiRecordEngine(typeof(Registro));

    var linhasDoArquivo = engine.ReadFile(nomeDoArquivo);

    var listaDeRegistros = new List<Registro>();

    foreach (var linha in linhasDoArquivo)
    {
        lista.Add((Registro)linha);
    }

    return listaDeRegistros;
}

// Combina a quantidade dos registros.
public List<Registro>() CombineQuantidades(List<Registro> registros)
{
    return registros.GroupBy(x => x.Codigo).Select(y => new Registro
    {
        Codigo = y.First().Codigo,
        Quantidade = y.Sum(z => z.Quantidade)
    }).ToList();
}

// Escreve os registros combinados somando a quantidade em um único arquivo.
public void EscrevaArquivoDosRegistrosCombinados(string nomeDoArquivoDeDestino, registros)
{
    MultiRecordEngine engine = new MultiRecordEngine(typeof(Registro));

    engine.AppendToFile(nomeDoArquivoDeDestino, registros);
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.