Conteúdos

Manipulando arquivos CSV usando SPL

Introdução

Todos já tivemos que, em algum momento na carreira, lidar com arquivos csv, seja para exportar dados simples em um arquivo, ou receber esses dados de algum lugar. Por ser tão comum, foi incluída na SPL, classes para lidar com esse tipo de arquivo.

SPL

SPL, é um conjunto de bibliotecas prontas, embutidas no core do PHP, de forma a facilitar problemas comuns a linguagem. Existem diversas classes e interfaces para as mais variadas soluções. Alguns exemplos são:

E o nosso assunto de hoje, a classe de manipulação de arquivos SplFileObject

DirectoryIterator

Essa classe provê diversas ferramentas para iteração de diretórios.

Segue um exemplo de listagem de arquivos em uma pasta usando somente as SPLs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

foreach (new DirectoryIterator(__DIR__) as $file) {
    if ($file->isDot()) {
        continue;
    }

    if ($file->getExtension() === 'csv') {
        var_dump($file);
    }
}

Nesse exemplo, usamos o iterador de diretório para listar arquivos em uma pasta (no caso a mesma pasta do script usando o __DIR__). Usamos o método isDot() para verificar se é uma listagem de pasta. Depois usamos o getExtension() para pegar a extensão do arquivo e verificamos se é csv.

É um exemplo bem simples de como podemos usar as SPLs para facilitar nossa vida. Segue o mesmo exemplo usando as funções padrões do PHP.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php

$directory = scandir(__DIR__);

foreach ($directory as $file) {
    if (in_array($file, ['.', '..'])) {
        continue;
    }

    $fileExplode = explode('.', $file);
    $extension = end($fileExplode);

    if ($extension === 'csv') {
        var_dump($file);
    }
}

Manipulando csv com a SplFileObject

A exemplo da DirectoryIterator, a SplFileObject possui métodos para manipulação de arquivos. O que essa classe faz internamente é um wrapper das funções de arquivos do PHP como fwrite e fgets.

Agora que vimos um pequeno resumo das SPLs e SplFileObject podemos ver como manipular arquivos csv.

Abaixo está um trecho de código em que criamos novas linhas no arquivo csv:

1
2
3
4
5
<?php

$csv = new SplFileObject(__DIR__ . '/arquivo1.csv', 'w');
$csv->setCsvControl($delimiter, $enclosure, $escape);
$csv->fputcsv(['Nome usuário', 'email@usuario.com']);

Instanciamos a classe SplFileObject com o endereço do arquivo e em modo de abertura

Modo de abertura

Usando o segundo parâmetro como 'w', o conteúdo do arquivo vai ser deletado e inserido um novo com o fputcsv(), caso queira adicionar e manter conteúdo antigo, usar o parâmetro 'a' de append.

Mais informações aqui: fopen()

Algumas implementações do arquivo csv diferem no uso dos delimitadores (o que separa cada coluna no csv), o encapsulador (caracter que engloba o dado), e o caracter de escape.

Nesse exemplo estou usando ponto-e-virgula como delimitador, aspas duplas como encapsulador e duas barras como escape.

A saída do arquivo csv vai ficar:

1
"Nome usuário";email@usuario.com

Perceba também que com o uso da SplFileObject não precisamos nos preocupar em fechar o arquivo (fclose()), a classe se encarrega disso.

Para a leitura seguimos esses passos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?php
$csv = new SplFileObject(__DIR__ . '/arquivo1.csv');
$csv->setFlags(
    SplFileObject::READ_CSV |
    SplFileObject::READ_AHEAD |
    SplFileObject::SKIP_EMPTY
);
$csv->setCsvControl($delimiter, $enclosure, $escape);

foreach ($csv as $line) {
    list($nome, $email) = $line;
    var_dump($nome, $email);
}

Precisamos passar os mesmos delimitadores, encapsulador e escape de quando o arquivo foi criado (talvez aqui você tenha que trabalhar um pouco, já que vai precisar verificar essas configurações no arquivo csv que está tentando abrir).

Também passamos algumas flags:

Flag Descrição
READ_CSV Faz a SplFileObject ler o conteúdo como um csv
READ_AHEAD Faz o PHP ler a próxima linha e voltar, assim ele consegue verificar se a próxima linha está vazia ou não por exemplo
SKIP_EMPTY Pula caso a linha esteja vazia

Assim evitamos de encontrar uma linha vazia e o PHP reclamar de algo.

Daí fizemos um foreach para percorrer cada linha e usamos o list() para passar cada posição do array $line para uma variável.

Aqui um script que lê todos os csvs de uma pasta e escreve seu conteudo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

foreach (new DirectoryIterator(__DIR__) as $file) {
    if ($file->isDot()) {
        continue;
    }

    if ($file->getExtension() === 'csv') {
        $delimiter = ';';
        $enclosure = '"';
        $escape = "\\";

        $csv = new SplFileObject($file->getPathname());
        $csv->setFlags(
            SplFileObject::READ_CSV |
            SplFileObject::READ_AHEAD |
            SplFileObject::SKIP_EMPTY
        );
        $csv->setCsvControl($delimiter, $enclosure, $escape);

        foreach ($csv as $line) {
            list($nome, $email) = $line;
            var_dump($nome, $email);
        }
    }
}

Com isso podemos manipular arquivos CSV usando essas bibliotecas padrões do PHP.