Dúvida sobre qual a melhor forma de separar dados em um log

Bom dia, prezados colegas. Lendo as questões sobre problemas com strings em softwares maiores, apresentadas por @hugokotsubo e @staroski no tópico Como fazer programa de digítos iguais? me surgiu uma curiosidade:

Existe uma rede de aviação simulada, chamada IVAO, onde pessoas podem se conectar para simular como pilotos ou controladores de tráfego aéreo. Inclusive dá para ver em tempo real as pessoas conectadas em:

https://webeye.ivao.aero/

Esses dados são oriundos de um arquivo de log que é atualizado a cada 3 minutos, com informações instantâneas, disponível em:

https://api.ivao.aero/getdata/whazzup/whazzup.txt

As informações são separadas por “:” (dois pontos). A minha intenção, por hobby, é pegar esses arquivos, ao longo de algumas horas (duração de alguns eventos aéreos específicos), para coletar as informações e elaborar relatórios do tipo “piloto P voou X horas de A para B”, “controlador C passou Y horas no serviço na estação Z”. Isso vai ajudar o pessoal do Depto de Eventos a conceder as medalhas a quem estava participando do evento.

Para isso preciso separar os dados e analisá-los, e a cada nova coleta de log (3 minutos depois), verificar se o Piloto P (ou o Controlador C) ainda está online, e se estiver, somar o tempo. Essa operação se repete, óbvio, até o final da conexão, para poder ter o tempo total de voo ou de controle de tráfego aéreo.

O que eu pensei em fazer é:

  1. Coletar os dados, salvando os arquivos em disco (software separado);

  2. Abrir os arquivos. Coletar o conteúdo para uma String ou StringBuilder;

  3. Separar os parâmetros com:

    StringBuilder conteudoDoArquivo = new StringBuilder();
    
    conteudoDoArquivo.append("texto do arquivo");
    
    String[] matrizDados = conteudoDoArquivo.toString().split(":");
    
  4. Atribuir os campos da matriz criada a um objeto (um objeto por pessoa conectada);

  5. Se aquela conexão já existe, apenas incrementar o tempo online;

  6. Ao final da análise dos arquivos, coletar algumas informações da página de perfil do usuário (como o nível de piloto e quais medalhas possui) e gerar um relatório pro Excel.

===========================================

A MINHA DÚVIDA É: qual a melhor forma de tratar essas informações, do item 3?

Existe algum procedimento que vocês recomendariam, que envolva menor gasto de memória e seja mais rápido? Ou o caminho é o tratamento convencional das strings?

===========================================

Desde já, muito obrigado pela ajuda.

1 curtida

Quanto ao formato do arquivo, cada linha é uma pessoa conectada?

Sim, correto. Cada linha é uma conexão.

Em alguns casos específicos, os staffs podem ter duas a três conexões simultâneas.

O formato do arquivo está descrito aqui: https://wiki.ivao.aero/en/home/devops/api/whazuup/file-format

Em qualquer aplicação que você queira medir algo ou saber se está gastando muita CPU/memória/qualquer outro recurso, o melhor é usar um profiler (aqui tem alguns exemplos) e testar em condições reais para saber se faz diferença ou não, onde estão os gargalos, etc. Dito isso…


De forma geral, eu não sei se é uma boa carregar todo o conteúdo do arquivo de uma vez (independente de usar String ou StringBuilder, pois o problema é carregar tudo de uma vez na memória - e depois fazer o split e criar um array gigante, o que na prática faz todo o conteúdo do arquivo ficar praticamente duplicado: uma vez na string gigante, outra nas substrings geradas pelo split).

Se cada linha é um registro específico, uma opção é ler o arquivo linha a linha, e a cada linha lida você cria o respectivo registro. Algo assim:

public class Registro {
    private String campo1;

    public Registro(String dados) { // recebe uma linha contendo os dados
        // separa os campos
        String[] campos = dados.split(":");

        // setar campos do registro
        this.campo1 = campos[0];
        etc...
    }
}

...
// ler arquivo linha a linha
para cada linha do arquivo {
    Registro reg = new Registro(linha);
    // faz o que precisar com o registro (até mesmo guardar em um array de registros, por exemplo)
}

Desta forma a linha é descartada logo depois que ela não é mais necessária (o mesmo vale para o array gerado pelo split, ele só é criado dentro do construtor, e como é uma variável local, será descartada logo depois que este terminar de executar).

Isso é garantido que seja melhor? Não sei, só medindo pra saber (ou seja, depende do caso concreto, em condições reais). Eu acho que fica melhor do que carregar o arquivo todo de uma vez (além de ficar mais organizado, que no fim deve ser o objetivo principal, antes de sair micro-otimizando o código).

Até porque o pool de strings do Java é meio que uma “caixa preta”, pode ser que no fim o consumo de memória nem mude tanto. E o split do Java, convenhamos, também não é o primor da performance (pois ele sempre cria uma regex internamente, mesmo se usarmos um texto fixo), então a eficiência do código também pode nem mudar tanto assim.

Enfim, só medindo mesmo pra saber…


Quanto ao outro tópico: gerar uma string não é um problema em si. O problema é gerar quando não precisa (ou gerar mais do que precisa). E isso vale para qualquer coisa, não somente strings :slight_smile:

1 curtida

@hugokotsubo obrigado pelos esclarecimentos.

Talvez para você ao invés de depois de fazer a leitura em um documento texto salvar novamente no documento texto para posterior leitura completa do programa, seja melhor usar banco de dados para armazenar o texto depois de ler o arquivo, ai a cada 3 minutos você pega o arquivo novo, lê, e grava as informações das variáveis dentro do banco estruturalmente. Pelo próprio banco você consegue dados como soma sem ter que carregar todos os dados e depois somar pelo programa, pois arquivo texto não é muito bom pra grande quantidade de dados.