12.01
2021
Arquivos .csv são muito utilizados para troca de dados, por conta da sua simplicidade de geração e interpretação. A rigor são linhas de texto com campos separadas por vírgulas (Comma-Separated Values – valores separados por vírgulas), mas é muito comum utilizar separação com ponto-e-vírgula.
Para os exemplos vamos utilizar o seguinte arquivo .csv:
Doe;John;Max;M;32;78.7
Doe;Jane;Reese;F;28;60.3
Braço;Joao;Sem;M;61;71.4
Ninguem;Joao;Sem;M;21;60.9
Couves;Jose;das;M;58;79.9
Um registro em cada linha, 6 campos: sobrenome, primeiro nome, nome do meio, gênero, idade e peso.
C e strtok
Em C ANSI a maioria das pessoas usaria strtok. Esta é uma função muito utilizada para fazer parser de campos. Sua utilização é muito simples, apesar de necessitar de alguns cuidados. Vamos avaliar a solução:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE *file = fopen("test.csv", "r");
if (!file) {
fprintf(stderr, "Cannot open file test.csv\n");
return 1;
}
while (!feof(file)) {
char line[256] = "";
char *field = NULL;
char lastname[64] = "";
char firstname[64] = "";
char middlename[64] = "";
int age = 0;
char gender = 'M';
double weight = 0.0;
fgets(line, 255, file);
line[strlen(line) - 1] = '\x0';
if (strlen(line) < 5)
break;
field = strtok(line, ";");
strcpy(lastname, (field? field: ""));
field = strtok(NULL, ";");
strcpy(firstname, (field? field: ""));
field = strtok(NULL, ";");
strcpy(middlename, (field? field: ""));
field = strtok(NULL, ";");
gender = (field? *field: 'M');
field = strtok(NULL, ";");
age = (field? atoi(field): 0);
field = strtok(NULL, ";");
weight = (field? atof(field): 0.0);
printf("%s, %s %s. %s, %d yo, %.2lf kg\n",
lastname, firstname, middlename,
(gender == 'M'? "male": "female"), age, weight
);
}
fclose(file);
}
Depois de abrir o arquivo e ler linha a linha com fgets, utilizamos a função strtok para recuperar cada campo. Note a necessidade de verificar se o ponteiro field é NULL para evitar referenciamento de ponteiro nulo.
C e fscanf
Muita gente não sabe utilizar scanf e suas funções derivadas. Uma pena pois perdem uma boa chance para criar um código mais limpo e mais sucinto.
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
FILE *file = fopen("test.csv", "r");
if (!file) {
fprintf(stderr, "Cannot ope file test.csv\n");
return 1;
}
while (!feof(file)) {
char lastname[64] = "";
char firstname[64] = "";
char middlename[64] = "";
int age = 0;
char gender = 'M';
double weight = 0.0;
int nfields = fscanf(file, "%63[^;];%63[^;];%63[^;];%c;%d;%lf\n",
lastname, firstname, middlename,
&gender, &age, &weight
);
if (nfields != 6)
break;
printf("%s, %s %s. %s, %d yo, %.1lf kg\n",
lastname, firstname, middlename,
(gender == 'M'? "Male": "Female"), age, weight
);
}
fclose(file);
return 0;
}
O código fica muito mais sucinto. Depois de abrir o arquivo o loop fica bem diferente, basta executar o fscanf para recuperar a linha e já fazer o parser dos valores, já atribuindo devidamente a cada variável.
O resultado final é exatamente o mesmo.
C++ e getline
Em C++ voltamos a uma situação parecida com C e strtok.
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <sstream>
int main() {
std::ifstream file;
file.open("test.csv", std::ifstream::in);
if (!file.is_open()) {
std::cerr << "Cannot open file test.csv" << std::endl;
return 1;
}
while (!file.eof()) {
char lastname[64] = "";
file.getline(lastname, 63, ';');
if (lastname[0] == '\x0')
break;
char middlename[64] = "";
file.getline(middlename, 63, ';');
char firstname[64] = "";
file.getline(firstname, 63, ';');
char field[16] = "";
file.getline(field, 15, ';');
char gender = field[0];
file.getline(field, 15, ';');
int age = std::stoi(field);
file.getline(field, 13, '\n');
double weight = std::stof(field);
std::cout << lastname << ", " << firstname << " " << middlename <<
". " << (gender == 'M'? "Male": "Female") << " " << age << " yo " <<
std::fixed << std::setprecision(1) << weight << " kg" << std::endl
;
}
file.close();
}
Após abrir o arquivo os campos são lidos diretamente do stream de dados utilizando getline, campos a campo, utilizando o separador ;, porém no último campo utilizamos o separador \n (fim de linha).
Em Resumo
A simplicidade da geração e da interpretação do formato .csv faz com, em especial em linguagens scripts, faz com que seja amplamente utilizado para troca de informações. Saber interpretá-lo de forma prática e rápida facilita o trabalho de qualquer desenvolvedor.