# CSV no Excel em português: ponto e vírgula, acentos e zeros à esquerda

> O Excel configurado em português usa ponto e vírgula como separador, porque a vírgula é o separador decimal, e só reconhece UTF-8 se o arquivo começar com o BOM. Este guia explica os problemas mais comuns com CSV no Brasil e mostra como gerar um CSV que o Excel abre certo e como ler o que ele salva, em JavaScript, Python e PHP.

- Endereço oficial: https://pradev.com.br/guias/csv-excel-ponto-e-virgula-acentos
- Tipo: Guia
- Publicado em: 2026-09-30
- Atualizado em: 2026-09-30
- Autor: Leonardo Gaertner (https://pradev.com.br/sobre)
- Ferramentas: [Conversor JSON para CSV](https://pradev.com.br/conversor-json-para-csv), [Conversor CSV para JSON](https://pradev.com.br/conversor-csv-para-json)

## Os três problemas de sempre

- Tudo numa coluna só: o arquivo usa vírgula, e o Excel em português espera ponto e vírgula. O separador segue a configuração regional do sistema, não o arquivo.
- Acentos quebrados (`JoÃ£o` no lugar de `João`): o arquivo está em UTF-8 sem BOM, e o Excel o lê como Windows-1252.
- Números errados: `1.5` vira data ou texto, e CEPs como `01310100` perdem o zero à esquerda.

O [conversor de JSON para CSV](https://pradev.com.br/conversor-json-para-csv) permite escolher o separador e gera o arquivo com BOM. O [conversor de CSV para JSON](https://pradev.com.br/conversor-csv-para-json) detecta o separador sozinho.

## Gerar CSV para o Excel em JavaScript

Três ajustes resolvem: ponto e vírgula como separador, vírgula nos números decimais e o BOM (`\uFEFF`) no começo. Campos com ponto e vírgula, aspas ou quebra de linha vão entre aspas, com as aspas internas duplicadas.

```javascript
// Monta um CSV que o Excel em português abre certo: ponto e vírgula, vírgula
// decimal e BOM para reconhecer o UTF-8
function csvParaExcel(linhas) {
  const colunas = Object.keys(linhas[0]);
  const celula = (v) => {
    const s = v == null ? '' : typeof v === 'number' ? String(v).replace('.', ',') : String(v);
    return /[;"\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
  };
  const texto = [colunas, ...linhas.map((l) => colunas.map((c) => l[c]))]
    .map((campos) => campos.map(celula).join(';'))
    .join('\r\n');
  return '﻿' + texto;
}
```

_JavaScript. Para baixar no navegador, crie um Blob com type "text/csv;charset=utf-8" a partir do texto._

## Ler e escrever em Python

O módulo `csv` cuida das aspas. A codificação `utf-8-sig` escreve o BOM na gravação e o ignora na leitura, e `newline=""` evita linhas em branco extras no Windows.

```python
import csv


def ler_csv_excel(caminho: str) -> list[dict]:
    # utf-8-sig ignora o BOM que o Excel grava no começo do arquivo
    with open(caminho, encoding="utf-8-sig", newline="") as f:
        return list(csv.DictReader(f, delimiter=";"))


def escrever_csv_excel(caminho: str, linhas: list[dict]) -> None:
    with open(caminho, "w", encoding="utf-8-sig", newline="") as f:
        w = csv.DictWriter(f, fieldnames=list(linhas[0]), delimiter=";")
        w.writeheader()
        w.writerows(linhas)
```

_Python_

## Ler em PHP

O `fgetcsv` não pula o BOM sozinho, então o exemplo confere os três primeiros bytes. O último argumento vazio desliga o caractere de escape próprio do PHP, que não faz parte do padrão CSV (e passá-lo explicitamente evita o aviso de depreciação no PHP 8.4).

```php
<?php
function lerCsvExcel(string $caminho): array
{
    $f = fopen($caminho, 'r');
    if (fread($f, 3) !== "\xEF\xBB\xBF") {
        rewind($f); // não tinha BOM: volta ao começo
    }
    $cabecalho = fgetcsv($f, null, ';', '"', '');
    $linhas = [];
    while (($campos = fgetcsv($f, null, ';', '"', '')) !== false) {
        if ($campos !== [null]) { // pula linhas em branco
            $linhas[] = array_combine($cabecalho, $campos);
        }
    }
    fclose($f);
    return $linhas;
}
```

_PHP_

## Cuidados

- Arquivos salvos no Excel como "CSV (separado por vírgulas)" costumam vir em Windows-1252, não em UTF-8. Se os acentos chegarem quebrados, leia com `encoding="cp1252"` no Python ou converta com `mb_convert_encoding` no PHP. A opção "CSV UTF-8" do Excel evita o problema.
- Valores que começam com `=`, `+`, `-` ou `@` podem ser executados como fórmula quando o arquivo é aberto (a chamada injeção de CSV). Em dados vindos de usuários, prefixe esses valores com um apóstrofo.
- Para manter zeros à esquerda (CEP, CPF), importe a coluna como texto no Excel. No arquivo, o valor já está certo: é a abertura que o transforma em número.
- Se o arquivo não for para o Excel, e sim para outro sistema, use o padrão: vírgula, UTF-8 sem BOM e ponto decimal.

## Perguntas frequentes

### Por que o CSV abre tudo em uma coluna no Excel?

O Excel usa o separador de listas da configuração regional. No Brasil, é ponto e vírgula, porque a vírgula é o separador decimal. Um CSV com vírgula abre tudo em uma coluna. Gere o arquivo com ponto e vírgula ou importe pelo menu Dados, opção De Texto/CSV, que permite escolher o separador.

### Como fazer o Excel abrir CSV com acentos?

Salve o arquivo em UTF-8 com BOM, os três bytes EF BB BF no começo. Sem eles, o Excel lê o arquivo como Windows-1252 e troca os acentos por símbolos estranhos.

### O que é o BOM?

É uma marca invisível no início do arquivo que indica a codificação. Em UTF-8, são os bytes EF BB BF. Muitos programas ignoram, mas o Excel depende dela para reconhecer o UTF-8.

### Como evitar que o Excel tire o zero à esquerda do CEP?

Na importação, defina a coluna como texto. Abrir o arquivo com duplo clique faz o Excel converter a coluna em número e perder os zeros, mesmo que o arquivo esteja certo.

## Fontes

- [RFC 4180: formato CSV](https://www.rfc-editor.org/rfc/rfc4180)
- [OWASP: CSV Injection](https://community.owasp.org/attacks/CSV_Injection)

## Guias relacionados

- [Como formatar e validar JSON em JavaScript, Python e PHP](https://pradev.com.br/guias/como-formatar-e-validar-json): Como deixar JSON legível e descobrir o erro de um JSON inválido em JavaScript, Python e PHP, com os erros mais comuns e as diferenças entre as linguagens.
