Um estacionamento possui uma cancela eletrônica que emite tíquetes com código de barras quando o usuário entra no estacionamento e outra cancela que lê os tíquetes quando os usuários saem do estacionamento. Cada uma das duas cancelas possui um sistema que grava em um arquivo texto a data, hora e o número identificador do código de barras (ID). A cancela de entrada gera o ID e o imprime no tíquete de estacionamento e a cancela de saída lê o ID do tíquete para gravá-lo no arquivo. Considere que os relógios de ambas as cancelas estão sincronizados. Segue um exemplo do conteúdo dos arquivos produzidos nas cancelas. Considere que no momento do processamento o estacionamento estava vazio.
Exemplo de arquivo criado pela cancela de entrada
data de entrada
hora de entrada
ID
08/01/2014
20:00:14
1042
08/01/2014
20:05:05
1043
08/01/2014
20:09:02
1044
08/01/2014
20:19:40
1045
[...]
Table 1: Exemplo de arquivo criado pela cancela de saída\captionsetup{labelformat=empty}
data de saída
hora de saída
ID
08/01/2014
1042
08/01/2014
1044
08/01/2014
1046
08/01/2014
1047
a) Escreva um programa que processe as informações desses dois arquivos, juntado os dados e produzindo um terceiro arquivo texto no formato indicado a seguir.
Table 2: Exemplo de arquivo produzido pelo processamento dos dados dos arquivos gerados pelas cancelas\captionsetup{labelformat=empty}
ID
data de entrada
hora de entrada data de saída
hora de saída
tempo de permanência (min)
00001042
00001043
00001044
00001045
b) Além de criar o arquivo processado, seu programa deverá exibir na tela algumas informações:
- o período dos dados que foram processados (data/hora inicial e data/hora final)
- a movimentação do estacionamento nesse período (número de carros)
- o tempo médio de permanência dos carros no estacionamento no período (em minutos)
Segue um exemplo:
Periodo…: 08/01/2014 20:00:14 a 09/01/2014 02:04:50
Numero de carros…: 20 carros
Tempo medio de permanencia…: 78.0 minutos
Obs: Para calcular a diferença em segundos entre duas datas/horas, utilize a função date_time_difference_in_seconds definida abaixo (copie e cole no seu programa). Para que esta função possa ser utilizada, deve-se importar a biblioteca time.h.
float date_time_difference_in_seconds( int start_day, int start_month, int start_year, int start_hour, int
start_minute, int start_second, int end_day, int end_month, int end_year, int end_hour, int end_minute, int end_second
) {
struct tm t1, t2;
t1.tm_year = start_year-1900; t1.tm_mon = start_month; t1.tm_mday = start_day; t1.tm_hour = start_hour;
t1.tm_min = start_minute; t1.tm_sec = start_second; t1.tm_isdst = 0; t2.tm_year = end_year-1900;
t2.tm_mon = end_mönth; t2.tm_mday = end_day; t2.tm_hour = end_hour; t2.tm_min = end_minute;
t2.tm_sec = end_second; t2.tm_isdst = 0;
return ( mktime( &t2 ) - mktime( &t1 ) );
}'
| data de entrada | hora de entrada | ID |
| 08/01/2014 | 20:00:14 | 1042 |
| 08/01/2014 | 20:05:05 | 1043 |
| 08/01/2014 | 20:09:02 | 1044 |
| 08/01/2014 | 20:19:40 | 1045 |
| [...] |
Table 1: Exemplo de arquivo criado pela cancela de saída
\captionsetup{labelformat=empty}
| data de saída | hora de saída | ID |
| 08/01/2014 |
|
1042 |
| 08/01/2014 |
|
1044 |
| 08/01/2014 |
|
1046 |
| 08/01/2014 |
|
1047 |
|
|
Table 2: Exemplo de arquivo produzido pelo processamento dos dados dos arquivos gerados pelas cancelas
\captionsetup{labelformat=empty}
| ID | data de entrada | hora de entrada data de saída | hora de saída | tempo de permanência (min) |
| 00001042 |
|
|
|
|
| 00001043 |
|
|
|
|
| 00001044 |
|
|
|
|
| 00001045 |
|
|
|
|
|
|
Periodo…: 08/01/2014 20:00:14 a 09/01/2014 02:04:50
Numero de carros…: 20 carros
Tempo medio de permanencia…: 78.0 minutos
Obs: Para calcular a diferença em segundos entre duas datas/horas, utilize a função date_time_difference_in_seconds definida abaixo (copie e cole no seu programa). Para que esta função possa ser utilizada, deve-se importar a biblioteca time.h.
float date_time_difference_in_seconds( int start_day, int start_month, int start_year, int start_hour, int
start_minute, int start_second, int end_day, int end_month, int end_year, int end_hour, int end_minute, int end_second
) {
struct tm t1, t2;
t1.tm_year = start_year-1900; t1.tm_mon = start_month; t1.tm_mday = start_day; t1.tm_hour = start_hour;
t1.tm_min = start_minute; t1.tm_sec = start_second; t1.tm_isdst = 0; t2.tm_year = end_year-1900;
t2.tm_mon = end_mönth; t2.tm_mday = end_day; t2.tm_hour = end_hour; t2.tm_min = end_minute;
t2.tm_sec = end_second; t2.tm_isdst = 0;
return ( mktime( &t2 ) - mktime( &t1 ) );
}'Passo 1
O que o enunciado pede e quais informações são dadas:
- Temos dois arquivos texto produzidos por duas cancelas:
- arquivo de entrada: cada linha contém data de entrada, hora de entrada e ID (o ID é gerado pela cancela de entrada);
- arquivo de saída: cada linha contém data de saída, hora de saída e ID (o ID é lido do tíquete na saída).
- Os relógios estão sincronizados (portanto as datas/horas podem ser comparadas diretamente).
- Na hora do processamento o estacionamento estava vazio (não precisamos tratar veículos que já estavam dentro antes do primeiro registro).
- Objetivos do programa:
- Produzir um terceiro arquivo texto juntando (fazendo o join) os dados de entrada e saída por ID, escrevendo para cada ID: ID com zero-padding (8 dígitos no exemplo), data/hora de entrada, data/hora de saída e tempo de permanência (em minutos).
- Exibir na tela:
- período dos dados processados: data/hora inicial e data/hora final (considerando todas as datas/horas lidas — tanto entradas quanto saídas);
- movimentação do estacionamento: número de carros processados (ou seja, número de pares entrada+saída produzidos no arquivo final);
- tempo médio de permanência dos carros no período (em minutos).
- Observações importantes extraídas do enunciado:
- O enunciado fornece uma função auxiliar para calcular diferença em segundos entre duas datas/horas, mas o código apresentado tem erros (variáveis com nomes errados e detalhe do mês). Devemos consertá-la antes de usar.
- Devemos tratar parsing das datas no formato
DD/MM/YYYYe horas no formatoHH:MM:SS.
- Hipóteses adotadas para a solução prática:
- Vamos produzir no arquivo de saída apenas os registros para os quais existe tanto a entrada quanto a saída (pares completos). Registros sem par podem ser opcionalmente registrados com saída vazia, mas, por simplicidade e porque o enunciado exige “juntando os dados”, vamos gerar linhas apenas para pares encontrados.
- O número de registros pode ser grande, mas vou propor uma solução simples e eficiente o suficiente para provas: ler todas as entradas em uma estrutura indexada por ID (usando um array + ordenação e busca binária), depois percorrer o arquivo de saídas buscando cada ID correspondente.
- Finais: não farei suposições sobre nomes dos arquivos além de escolher nomes claros no programa (por exemplo
entrada.txt,saida.txt,processado.txt).
Passo 2
Decisão do método e estruturas de dados (por que e como):
- Método escolhido:
- Ler todo o arquivo de entradas em memória em um vetor de structs (cada struct guarda ID e data/hora de entrada).
- Ordenar esse vetor por ID (usar
qsort) para permitir busca rápida por ID combsearch. - Abrir o arquivo de saídas e para cada linha parsear ID e data/hora de saída, buscar a entrada correspondente no vetor ordenado. Se existir, criar um registro combinado (entrada+saída) e gravá-lo no arquivo de saída processado; calcular o tempo de permanência e acumular para média.
- Enquanto lemos entradas e saídas iremos também atualizar o menor e o maior timestamp encontrados para calcular o período.
- Por que esse método:
- É simples de implementar com mecanismos de C padrão (
qsort,bsearch) e suficiente para a maioria dos tamanhos de entrada em prova. - Alternativa seria usar uma tabela hash (mais trabalho para implementar em C puro) ou armazenar saídas e depois fazer merge sort; optei por ordenar entradas e usar busca binária — elegante e didático.
- É simples de implementar com mecanismos de C padrão (
- Estruturas principais:
typedef struct { int id; int day, month, year, hour, minute, second; } Registro;- Vetor dinâmico de
Registropara entradas. - Funções auxiliares:
- parse de linhas
DD/MM/YYYYeHH:MM:SSusandosscanf. - comparação de
Registroporidparaqsort/bsearch. - função corrigida
date_time_difference_in_seconds(...)para usarstruct tmemktime. - função para comparar dois timestamps (para saber min/max).
- função para formatar ID com zero-padding de 8 dígitos ao escrever.
- parse de linhas
Passo 3
Detalhamento de parsing e manipulação de datas/horas (aulas rápidas e dicas para a prova):
- Como ler as strings de data e hora:
- A forma clássica em C é usar
fgetspara ler cada linha e depoissscanfcom um formato que capture os campos numéricos:sscanf(line, "%d/%d/%d %d:%d:%d %d", &day, &month, &year, &hour, &minute, &second, &id);- No enunciado os campos podem estar separados por espaços ou em colunas; adaptar o
sscanfconforme o formato real do arquivo. Se data e hora estiverem em colunas separadas, chamarsscanf(line, "%d/%d/%d %d:%d:%d %d", ...)ainda funciona se elas estiverem com espaço entre.
- Sempre verificar o valor de retorno de
sscanfpara confirmar quantos campos foram lidos.
- A forma clássica em C é usar
- Notas sobre
struct tmemktime:struct tmtem campos:tm_year(anos desde 1900),tm_mon(mês de 0 a 11),tm_mday(dia do mês 1…31),tm_hour,tm_min,tm_sec.- Ao preencher
struct tmparamktime, ajustartm_year = year - 1900;etm_mon = month - 1;. mktimeretornatime_trepresentando segundos desde epoch; a diferençamktime(&t2) - mktime(&t1)dá diferença em segundos (tipodouble/time_t), que pode ser convertida parafloat.
- Correção e reescrita da função do enunciado:
- O enunciado tinha erros:
t1.tm_mon = start_month;(deveria serstart_month - 1), variáveis com nomes errados (t2.tm_mon = end_mönth;com acento), etc. Vamos fornecer a versão correta:- preencher
t1et2corretamente, setartm_isdst = -1(deixar o sistema decidir, ou0se souber que DST não se aplica). - retornar
(float)(difftime(mktime(&t2), mktime(&t1)))ou(mktime(&t2) - mktime(&t1))convertido.
- preencher
- O enunciado tinha erros:
- Cálculo do tempo em minutos:
- Primeiro obter diferença em segundos com a função corrigida.
- Converter para minutos:
minutes = seconds / 60.0. - Para média: somar todas as durações em minutos e dividir pelo número de veículos (pares).
- Como comparar datetimes para achar período:
- Escrever função
int cmp_datetime(Registro *a, Registro *b)que compara ano, mês, dia, hora, minuto, segundo nesta ordem. Ou converter paratime_tviastruct tme comparartime_t. - Manter variáveis
min_datetimeemax_datetimeatualizadas ao ler entradas e saídas (usar as funções de comparação).
- Escrever função
Passo 4
Leitura, junção e escrita passo a passo (procedimento que você deve seguir na prova):
- Abrir arquivo de entradas (
entrada.txt) em modo leitura; se falhar, reportar erro e sair. - Ler todas as linhas do arquivo de entradas:
- para cada linha: parsear
day, month, year, hour, minute, second, id. - construir um
Registrocontendo esses campos (entrada) e guardar no vetor de entradas. - atualizar
min_datetimeemax_datetimeconsiderando essa entrada.
- para cada linha: parsear
- Fechar o arquivo de entradas.
- Ordenar o vetor de entradas por
idcomqsort. - Abrir arquivo de saídas (
saida.txt) em modo leitura e arquivo processado (processado.txt) em modo escrita. - Inicializar contadores:
num_pares = 0; soma_minutos = 0.0. - Para cada linha do arquivo de saídas:
- parsear
day_out, month_out, year_out, hour_out, minute_out, second_out, id_out. - atualizar
min_datetimeemax_datetimecom a saída também. - montar uma chave
Registrocomid_oute usarbsearchno vetor de entradas:- se encontrado:
- calcular
seconds = date_time_difference_in_seconds(entry_day,..., exit_day,...)usando a função corrigida; minutes = seconds / 60.0;- escrever no arquivo
processado.txtuma linha com:- ID formatado com zero-padding de 8 dígitos (usar
fprintf(out, "%08d ...", id)), - data/hora entrada e saída no mesmo formato do enunciado,
- (opcionalmente) o tempo em minutos (o enunciado pedia tempo de permanência (min) na tabela de exemplo — preserve esse campo).
- ID formatado com zero-padding de 8 dígitos (usar
- incrementar
num_pares++esoma_minutos += minutes.
- calcular
- se não encontrado: ignorar (ou registrar em log se quiser depurar).
- se encontrado:
- parsear
- Ao terminar, fechar arquivos.
- Calcular tempo médio: se
num_pares > 0,media = soma_minutos / num_pares; senãomedia = 0. - Exibir na tela:
- período: imprimir
min_datetimeemax_datetimeno formatoDD/MM/YYYY HH:MM:SS. - número de carros:
num_pares. - tempo médio de permanência:
media(com uma casa decimal, por exemplo).
- período: imprimir
- Encerrar.
Passo 5
Tratamento de casos extremos e boas práticas (dicas para a prova):
- Se uma saída aparece antes da entrada correspondente (por erro de dados), a função de diferença em segundos pode retornar valor negativo; você pode:
- optar por ignorar pares onde saída < entrada e emitir aviso;
- ou considerar o valor absoluto se o enunciado permitir. Eu recomendo detectar e emitir aviso (ou tratar como dado sujo).
- Verifique sempre o retorno de
fopen,fgets,sscanf,malloc/realloc. - Para arquivos grandes: ler entradas e saídas em streaming com uma tabela hash é mais eficiente; em prova, explicado esse método e implementado o método por ordenação é suficiente.
- Formatação do ID: usar
"%08d"para zero-padding até 8 dígitos. - Ao usar
mktime, inicialize todos os campos destruct tmcom0antes de preencher, para evitar lixo em campos.
Passo 6
Exemplo de comportamento final esperado (com base no enunciado):
- O arquivo
processado.txtterá uma linha por veículo pareado, por exemplo:00001042 08/01/2014 20:00:14 08/01/2014 21:15:08 75
(onde o último campo75seria tempo de permanência em minutos arredondado ou truncado conforme sua escolha).
- Na tela será impresso algo como:
Periodo.....................: 08/01/2014 20:00:14 a 09/01/2014 02:04:50Numero de carros............: 20 carrosTempo medio de permanencia..: 78.0 minutos
Passo 7
Apresentação do programa completo em C (use este código na prova, copie e cole, compile com
gcc):Resposta
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct {
int id;
int day, month, year;
int hour, minute, second;
} Registro;
int cmp_registro_by_id(const void *a, const void *b) {
const Registro *ra = (const Registro *)a;
const Registro *rb = (const Registro *)b;
if (ra->id < rb->id) return -1;
if (ra->id > rb->id) return 1;
return 0;
}
int cmp_datetime(const Registro *a, const Registro *b) {
if (a->year != b->year) return (a->year < b->year) ? -1 : 1;
if (a->month != b->month) return (a->month < b->month) ? -1 : 1;
if (a->day != b->day) return (a->day < b->day) ? -1 : 1;
if (a->hour != b->hour) return (a->hour < b->hour) ? -1 : 1;
if (a->minute != b->minute) return (a->minute < b->minute) ? -1 : 1;
if (a->second != b->second) return (a->second < b->second) ? -1 : 1;
return 0;
}
float date_time_difference_in_seconds(int start_day, int start_month, int start_year, int start_hour, int
start_minute, int start_second, int end_day, int end_month, int end_year, int end_hour, int end_minute, int end_second) {
struct tm t1 = {0}, t2 = {0};
t1.tm_year = start_year - 1900;
t1.tm_mon = start_month - 1;
t1.tm_mday = start_day;
t1.tm_hour = start_hour;
t1.tm_min = start_minute;
t1.tm_sec = start_second;
t1.tm_isdst = -1;
t2.tm_year = end_year - 1900;
t2.tm_mon = end_month - 1;
t2.tm_mday = end_day;
t2.tm_hour = end_hour;
t2.tm_min = end_minute;
t2.tm_sec = end_second;
t2.tm_isdst = -1;
time_t tt1 = mktime(&t1);
time_t tt2 = mktime(&t2);
return (float)difftime(tt2, tt1);
}
void format_datetime(char *buf, size_t bufsize, const Registro *r) {
snprintf(buf, bufsize, "%02d/%02d/%04d %02d:%02d:%02d",
r->day, r->month, r->year, r->hour, r->minute, r->second);
}
int main() {
const char *file_entrada = "entrada.txt";
const char *file_saida = "saida.txt";
const char *file_processado = "processado.txt";
FILE *fin = fopen(file_entrada, "r");
if (!fin) {
fprintf(stderr, "Erro ao abrir %s\n", file_entrada);
return 1;
}
// Ler entradas dinamicamente
Registro *entradas = NULL;
size_t cap = 0, n_entradas = 0;
char line[256];
Registro min_dt, max_dt;
int first_dt = 1;
while (fgets(line, sizeof(line), fin)) {
// Tentar ler linha no formato: DD/MM/YYYY HH:MM:SS ID
int d, m, y, hh, mm, ss, id;
int read = sscanf(line, " %d/%d/%d %d:%d:%d %d", &d, &m, &y, &hh, &mm, &ss, &id);
if (read < 7) {
// Tentar outra possibilidade: data e hora separados por tabs/espacos e ID em outra coluna
// Exemplo: "08/01/2014 20:00:14 1042"
read = sscanf(line, " %d/%d/%d %d:%d:%d %d", &d, &m, &y, &hh, &mm, &ss, &id);
}
if (read >= 7) {
if (n_entradas == cap) {
cap = (cap == 0) ? 128 : cap * 2;
entradas = realloc(entradas, cap * sizeof(Registro));
if (!entradas) { fprintf(stderr, "Memoria insuficiente\n"); fclose(fin); return 1; }
}
entradas[n_entradas].id = id;
entradas[n_entradas].day = d;
entradas[n_entradas].month = m;
entradas[n_entradas].year = y;
entradas[n_entradas].hour = hh;
entradas[n_entradas].minute = mm;
entradas[n_entradas].second = ss;
// atualizar min/max
if (first_dt) {
min_dt = entradas[n_entradas];
max_dt = entradas[n_entradas];
first_dt = 0;
} else {
if (cmp_datetime(&entradas[n_entradas], &min_dt) < 0) min_dt = entradas[n_entradas];
if (cmp_datetime(&entradas[n_entradas], &max_dt) > 0) max_dt = entradas[n_entradas];
}
n_entradas++;
}
}
fclose(fin);
// Ordenar entradas por ID
if (n_entradas > 0) qsort(entradas, n_entradas, sizeof(Registro), cmp_registro_by_id);
FILE *fout = fopen(file_processado, "w");
if (!fout) {
fprintf(stderr, "Erro ao abrir %s para escrita\n", file_processado);
free(entradas);
return 1;
}
FILE *fs = fopen(file_saida, "r");
if (!fs) {
fprintf(stderr, "Erro ao abrir %s\n", file_saida);
free(entradas);
fclose(fout);
return 1;
}
size_t num_pares = 0;
double soma_minutos = 0.0;
while (fgets(line, sizeof(line), fs)) {
int d, m, y, hh, mm, ss, id;
int read = sscanf(line, " %d/%d/%d %d:%d:%d %d", &d, &m, &y, &hh, &mm, &ss, &id);
if (read < 7) {
// Tentar formato com espaços estranhos, ou com parênteses; tentar extrair números de qualquer jeito
read = sscanf(line, " %d/%d/%d %d:%d:%d %d", &d, &m, &y, &hh, &mm, &ss, &id);
}
if (read >= 7) {
Registro chave;
chave.id = id;
Registro *found = bsearch(&chave, entradas, n_entradas, sizeof(Registro), cmp_registro_by_id);
// atualizar min/max com a saída
Registro reg_out;
reg_out.id = id; reg_out.day = d; reg_out.month = m; reg_out.year = y;
reg_out.hour = hh; reg_out.minute = mm; reg_out.second = ss;
if (first_dt) {
min_dt = reg_out;
max_dt = reg_out;
first_dt = 0;
} else {
if (cmp_datetime(®_out, &min_dt) < 0) min_dt = reg_out;
if (cmp_datetime(®_out, &max_dt) > 0) max_dt = reg_out;
}
if (found) {
float secs = date_time_difference_in_seconds(found->day, found->month, found->year, found->hour, found->minute, found->second,
reg_out.day, reg_out.month, reg_out.year, reg_out.hour, reg_out.minute, reg_out.second);
double minutos = secs / 60.0;
// Se preferir, pode ignorar pares com tempo negativo:
// if (secs < 0) continue;
// Gravar no arquivo processado no formato pedido
char buf_ent[64], buf_sai[64];
format_datetime(buf_ent, sizeof(buf_ent), found);
format_datetime(buf_sai, sizeof(buf_sai), ®_out);
// Exemplo de linha: ID(8 dígitos) data_ent hora_ent data_sai hora_sai tempo_min (tempo em minutos com 1 casa decimal)
fprintf(fout, "%08d %s %s %.1f\n", found->id, buf_ent, buf_sai, minutos);
num_pares++;
soma_minutos += minutos;
} else {
// saída sem entrada correspondente: ignorar (ou logar)
}
}
}
fclose(fs);
fclose(fout);
free(entradas);
double media = (num_pares > 0) ? (soma_minutos / (double)num_pares) : 0.0;
// Imprimir resultado conforme exemplo
char buf_min[64], buf_max[64];
format_datetime(buf_min, sizeof(buf_min), &min_dt);
format_datetime(buf_max, sizeof(buf_max), &max_dt);
printf("Periodo.....................: %s a %s\n", buf_min, buf_max);
printf("Numero de carros............: %zu carros\n", num_pares);
printf("Tempo medio de permanencia..: %.1f minutos\n", media);
return 0;
}