C# Cultura
Desde as primeiras aulas, o Console.WriteLine(3.5) mostra 3,5, com vírgula, e o dinheiro aparece como R$ 1.234,50. Nos Estados Unidos, o mesmo programa mostra 3.5 e $1,234.50. Quem decide é a cultura: o conjunto de costumes de um país para escrever números, datas e moedas. Nesta aula você vai ver de onde ela vem, como escrever números do jeito que outros programas entendem, e por que double.Parse("3.5") dá 35 num computador brasileiro. Outras aulas do guia trazem você para cá quando aparece esse 3,5.
- O que é a cultura, de onde ela vem (
CultureInfo.CurrentCulturee a variávelLANG) e o que ela muda. - Como o mesmo número e a mesma data aparecem em vários países.
- Escrever números para máquinas com
ToString(CultureInfo.InvariantCulture). - Ler números com ponto:
double.Parse("3.5", CultureInfo.InvariantCulture)e oTryParsecom cultura. - Usar a cultura de um país específico:
new CultureInfo("en-US"). - Mudar a cultura do programa inteiro, e como a cultura muda a ordem de textos com acentos.
Esta aula usa Formatar Números, Parse e TryParse e Datas e Horas. Os programas rodam com dotnet run na pasta ~/cs do computador da Ana, que está em português do Brasil.
A ideia: cada país escreve de um jeito
No Brasil, "mil duzentos e trinta e quatro reais e cinquenta centavos" se escreve R$ 1.234,50: ponto separa os milhares e vírgula separa os centavos. Nos Estados Unidos é o contrário: $1,234.50. E a data de hoje é 17/10/2026 aqui e 10/17/2026 lá, com o mês primeiro.
O número guardado na memória é o mesmo; muda só o jeito de escrever e de ler o texto. A cultura funciona como um tradutor de costumes: cada vez que um número ou uma data vira texto (no WriteLine, no ToString, na interpolação) ou um texto vira número (no Parse), o .NET pergunta ao tradutor como fazer.
| O que a cultura decide | pt-BR | en-US |
|---|---|---|
| Separador decimal | 3,5 | 3.5 |
| Separador de milhar | 1.234 | 1,234 |
Moeda ("C") | R$ 1.234,50 | $1,234.50 |
Data curta ("d") | 17/10/2026 | 10/17/2026 |
| Nomes de dias e meses | sábado, outubro | Saturday, October |
| Ordem alfabética e maiúsculas | regras de cada língua (seção 6) | |
1. A cultura atual: CultureInfo.CurrentCulture
A classe CultureInfo ("informação de cultura") fica no namespace System.Globalization (de "globalização"), que precisa de using:
using System.Globalization;
CultureInfo atual = CultureInfo.CurrentCulture;
Console.WriteLine(atual.Name);
Console.WriteLine(atual.DisplayName);
Console.WriteLine($"Decimal: '{atual.NumberFormat.NumberDecimalSeparator}'");
Console.WriteLine($"Milhar: '{atual.NumberFormat.NumberGroupSeparator}'");
Console.WriteLine($"Moeda: '{atual.NumberFormat.CurrencySymbol}'");
$ dotnet run atual.cs pt-BR português (Brasil) Decimal: ',' Milhar: '.' Moeda: 'R$' $ LANG=en_US.UTF-8 dotnet run atual.cs « o mesmo programa, "como se" o computador fosse americano en-US English (United States) Decimal: '.' Milhar: ',' Moeda: '$'
- CultureInfo.CurrentCulture
- "A cultura atual": a que o programa usa quando você não diz nenhuma. Ela vem do sistema operacional. No Linux, da variável de ambiente
LANG, que no Ubuntu da Ana valept_BR.UTF-8; no Windows e no macOS, das configurações de região. - atual.Name
- O nome da cultura: a língua (
pt, português) e o país (BR, Brasil).pt-PTé o português de Portugal,en-USo inglês dos Estados Unidos,en-GBo do Reino Unido. - atual.DisplayName
- O nome para mostrar, na língua da própria cultura.
- atual.NumberFormat
- As regras de números da cultura:
NumberDecimalSeparator(separador decimal),NumberGroupSeparator(de milhar),CurrencySymbol(símbolo da moeda). - LANG=en_US.UTF-8 dotnet run atual.cs
- No terminal do Linux,
VARIAVEL=valor comandoroda o comando com a variável trocada, só daquela vez (o Guia Bash explica). Foi o suficiente para o programa virar americano.
É por isso que um programa pode funcionar no seu computador e dar resultado diferente no de outra pessoa, ou num servidor configurado em inglês: o código é o mesmo, a cultura não.
2. O mesmo valor em vários países
O ToString de números e datas aceita, depois do formato, a cultura que você quer usar:
using System.Globalization;
decimal preco = 1234.5m;
DateTime data = new DateTime(2026, 10, 17);
string[] nomes = ["pt-BR", "pt-PT", "en-US", "en-GB", "es-ES", "de-DE", "ja-JP"];
foreach (string nome in nomes)
{
CultureInfo c = new CultureInfo(nome);
Console.WriteLine($"{nome}: {preco.ToString("N2", c)} | {preco.ToString("C", c)} | {data.ToString("d", c)} | {data.ToString("D", c)}");
}
CultureInfo inv = CultureInfo.InvariantCulture;
Console.WriteLine($"Invariante: {preco.ToString("N2", inv)} | {preco.ToString("C", inv)} | {data.ToString("d", inv)}");
$ dotnet run paises.cs pt-BR: 1.234,50 | R$ 1.234,50 | 17/10/2026 | sábado, 17 de outubro de 2026 pt-PT: 1 234,50 | 1 234,50 € | 17/10/2026 | sábado, 17 de outubro de 2026 en-US: 1,234.50 | $1,234.50 | 10/17/2026 | Saturday, October 17, 2026 en-GB: 1,234.50 | £1,234.50 | 17/10/2026 | Saturday, 17 October 2026 es-ES: 1.234,50 | 1.234,50 € | 17/10/2026 | sábado, 17 de octubre de 2026 de-DE: 1.234,50 | 1.234,50 € | 17.10.2026 | Samstag, 17. Oktober 2026 ja-JP: 1,234.50 | ¥1,235 | 2026/10/17 | 2026年10月17日土曜日 Invariante: 1,234.50 | ¤1,234.50 | 10/17/2026
- new CultureInfo("en-US")
- Cria a cultura pelo nome. (
CultureInfo.GetCultureInfo("en-US")faz o mesmo e reaproveita o objeto, bom para usar muitas vezes.) - preco.ToString("C", c)
- O formato
"C"(moeda) com as regras da culturac: símbolo, posição do símbolo e separadores. Repare no Japão: o iene não tem centavos, e1234,5foi arredondado para1,235. - pt-PT: 1 234,50
- Em Portugal, o milhar é separado por um espaço especial, que não quebra a linha (não é o espaço comum da barra de espaço).
- CultureInfo.InvariantCulture
- A cultura invariante, "que não varia": não é de país nenhum. Usa ponto nos decimais, vírgula no milhar, datas mês/dia/ano e o símbolo genérico de moeda
¤. Ela é igual em todo computador, em qualquer configuração. Para que serve? Seção 3.
As linhas de pt-BR, pt-PT, en-US, en-GB, es-ES e de-DE e a invariante saem iguais às do terminal. A do Japão, não: o editor não traz os dados de japonês, e a linha sai ja-JP: 1.234,50 | ¥ 1.235 | 17/10/2026 | sábado, 17 de outubro de 2026 (só o iene muda; o resto fica no formato do Brasil). Para ver as culturas da Ásia, rode no terminal.
Atenção: "C" só troca o desenho da moeda, não converte o valor. 1234,5 reais escritos com $ continuam sendo 1234,5, e não viram dólares.
3. Escrever para máquinas: InvariantCulture
Para pessoas, a cultura atual é ótima: cada uma vê os números do jeito do seu país. Mas quando o texto vai para outro programa (um arquivo CSV, uma planilha que será importada, um site, um banco de dados), o formato tem de ser sempre o mesmo, em qualquer computador. Para isso existe a cultura invariante:
using System.Globalization;
double nota = 3.5;
Console.WriteLine(nota);
Console.WriteLine(nota.ToString());
Console.WriteLine(nota.ToString(CultureInfo.InvariantCulture));
Console.WriteLine($"Nota: {nota}");
Console.WriteLine(string.Create(CultureInfo.InvariantCulture, $"Nota: {nota}"));
Console.WriteLine(FormattableString.Invariant($"Nota: {nota}"));
$ dotnet run invariante.cs 3,5 3,5 3.5 Nota: 3,5 Nota: 3.5 Nota: 3.5
- Console.WriteLine(nota) e nota.ToString()
- Os dois usam a cultura atual:
3,5. - nota.ToString(CultureInfo.InvariantCulture)
- Com a cultura invariante:
3.5, com ponto, em qualquer computador. Também dá para juntar um formato:nota.ToString("F2", CultureInfo.InvariantCulture)dá3.50. - $"Nota: {nota}"
- A interpolação também usa a cultura atual.
- string.Create(CultureInfo.InvariantCulture, $"…")
- Monta a string interpolada inteira com a cultura que você escolher .NET 6. Todos os números de dentro saem com ponto.
- FormattableString.Invariant($"…")
- Um atalho mais antigo para o mesmo, só com a cultura invariante.
Missão: a Ana quer gerar um CSV, com os campos separados por vírgula, para a escola importar. Mas a vírgula da nota se mistura com a do separador: Ana,8,5 parece ter três colunas. Escreva a nota com notas[i].ToString(CultureInfo.InvariantCulture) para as linhas ficarem Ana,8.5, Bruno,7 e Carla,9.25.
using System.Globalization;
string[] alunos = ["Ana", "Bruno", "Carla"];
double[] notas = [8.5, 7, 9.25];
for (int i = 0; i < alunos.Length; i++)
{
Console.WriteLine($"{alunos[i]},{notas[i]}");
}
using System.Globalization;
string[] alunos = ["Ana", "Bruno", "Carla"];
double[] notas = [8.5, 7, 9.25];
for (int i = 0; i < alunos.Length; i++)
{
Console.WriteLine($"{alunos[i]},{notas[i].ToString(CultureInfo.InvariantCulture)}");
}
Outra saída, muito usada nas planilhas brasileiras, é trocar o separador do CSV por ;, como nas aulas Arquivos e Ler e Escrever. Mesmo assim, um arquivo que vai para outro programa fica mais seguro com a cultura invariante, porque assim ele é igual em qualquer computador.
4. Ler números: Parse com cultura
O caminho de volta tem a mesma regra: o Parse lê o texto com os costumes da cultura atual, se você não disser outra. E é aqui que mora o erro mais traiçoeiro desta aula:
using System.Globalization;
Console.WriteLine(double.Parse("3,5"));
Console.WriteLine(double.Parse("3.5"));
Console.WriteLine(double.Parse("1.234,5"));
Console.WriteLine(double.Parse("3.5", CultureInfo.InvariantCulture));
if (double.TryParse("3.5", NumberStyles.Float, CultureInfo.InvariantCulture, out double x))
{
Console.WriteLine($"Li {x}");
}
$ dotnet run ler.cs 3,5 35 1234,5 3,5 Li 3,5
- double.Parse("3,5")
- Em pt-BR, a vírgula é o separador decimal: três e meio.
- double.Parse("3.5") → 35
- Em pt-BR, o ponto é o separador de milhar, e o
Parseaceita e ignora separadores de milhar (como em1.234). Sobrou35. Nenhum erro, nenhum aviso: só um número dez vezes maior. Uma nota 8.5 vira 85, um preço de 2.50 vira 250. - double.Parse("1.234,5")
- O formato brasileiro completo, com milhar e decimal:
1234,5. - double.Parse("3.5", CultureInfo.InvariantCulture)
- Diz ao
Parseque o texto está no formato invariante, com ponto: três e meio, mostrado depois como3,5peloWriteLine. - double.TryParse("3.5", NumberStyles.Float, CultureInfo.InvariantCulture, out double x)
- A versão segura, sem exceção. Com cultura, o
TryParsepede também o estilo (NumberStyles):Floataceita sinal, decimais e expoente (1e3);Numberaceita também o separador de milhar. Paradecimal, useNumberStyles.Number.
| O texto vem de… | Leia com |
|---|---|
| Uma pessoa, digitando no seu programa | A cultura atual: double.TryParse(texto, out double x) |
| Um arquivo, um site ou outro programa, com ponto | CultureInfo.InvariantCulture |
| Um sistema de um país conhecido | A cultura dele: new CultureInfo("en-US") |
| JSON | O System.Text.Json já usa ponto sempre (aula JSON) |
5. A cultura de um país: new CultureInfo
A lojinha da Ana comprou de um fornecedor dos Estados Unidos, e o pedido veio no formato de lá, com vírgula no milhar e ponto nos centavos:
$ cat pedido.txt Notebook;1,299.90 Mouse;49.90
using System.Globalization;
CultureInfo eua = new CultureInfo("en-US");
decimal total = 0;
foreach (string linha in File.ReadAllLines("pedido.txt"))
{
string[] campos = linha.Split(';');
decimal preco = decimal.Parse(campos[1], eua);
Console.WriteLine($"{campos[0]}: {preco:C}");
total += preco;
}
Console.WriteLine($"Total: {total:C}");
$ dotnet run fornecedor.cs Notebook: R$ 1.299,90 Mouse: R$ 49,90 Total: R$ 1.349,80
- decimal.Parse(campos[1], eua)
- Lê o texto com os costumes americanos: em
1,299.90, a vírgula é o milhar e o ponto é o decimal. - {preco:C}
- Mostra com a cultura atual, a brasileira:
R$ 1.299,90. Ler com uma cultura e mostrar com outra é normal: o número no meio não tem cultura nenhuma.
6. Mudar a cultura do programa, e os textos
Você pode trocar a cultura atual de dentro do programa. Tudo o que vier depois usa a nova:
using System.Globalization;
Console.WriteLine(1234.5m.ToString("C"));
CultureInfo.CurrentCulture = new CultureInfo("en-US");
Console.WriteLine(3.5);
Console.WriteLine(1234.5m.ToString("C"));
$ dotnet run trocar.cs R$ 1.234,50 3.5 $1,234.50
Faça isso só quando o programa inteiro deve seguir um país (por exemplo, um relatório que sempre sai em formato brasileiro, até no servidor em inglês). Para um ponto só do programa, passe a cultura no ToString e no Parse, como nas seções anteriores: fica claro para quem lê o código.
Ordenar e comparar textos
A cultura também conhece as regras de ordem alfabética da língua, inclusive os acentos. Já a comparação ordinal ("pela ordem dos números") compara os códigos das letras, sem conhecer língua nenhuma:
string[] nomes = ["Zé", "ana", "Álvaro", "Bruno", "Érica", "carla"];
Console.WriteLine(string.Join(" ", nomes.Order()));
Console.WriteLine(string.Join(" ", nomes.Order(StringComparer.Ordinal)));
Console.WriteLine(string.Equals("SÃO PAULO", "são paulo", StringComparison.CurrentCultureIgnoreCase));
Console.WriteLine(string.Equals("foto.JPG", "foto.jpg", StringComparison.OrdinalIgnoreCase));
$ dotnet run ordem.cs Álvaro ana Bruno carla Érica Zé Bruno Zé ana carla Álvaro Érica True True
- nomes.Order()
- Ordena com a cultura atual: a ordem de um dicionário em português, com
Álvarojunto dosAe sem separar maiúsculas de minúsculas. - Order(StringComparer.Ordinal)
- Pelos códigos das letras: todas as maiúsculas sem acento vêm antes das minúsculas, e as letras acentuadas vão para o fim. Para mostrar a pessoas, parece errado.
- StringComparison.CurrentCultureIgnoreCase
- Compara com as regras da língua, sem diferenciar maiúsculas:
SÃO PAULOé igual asão paulo. - StringComparison.OrdinalIgnoreCase
- Compara os códigos, sem diferenciar maiúsculas. É o certo para coisas de computador: nomes de arquivo, extensões, comandos, chaves. É mais rápido e não muda de país para país.
Alguns servidores e contêineres (como imagens Docker bem pequenas) rodam o .NET no modo de globalização invariante, sem os dados das culturas. Nele, a cultura atual é sempre a invariante, e o programa mostra 3.5 mesmo com LANG=pt_BR.UTF-8. Dá para ver esse modo com a variável DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1:
$ DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 dotnet run invariante.cs 3.5 3.5 3.5 Nota: 3.5 Nota: 3.5 Nota: 3.5
Mais um motivo para passar a cultura sempre que o formato importa.
Qual cultura usar
| Situação | Cultura | Exemplo |
|---|---|---|
| Mostrar para a pessoa que usa o programa | A atual (não passe nada) | $"{preco:C}" |
| Ler o que a pessoa digitou | A atual | decimal.TryParse(texto, out decimal p) |
| Gravar ou ler arquivos, CSV, dados para outros programas | CultureInfo.InvariantCulture | nota.ToString(CultureInfo.InvariantCulture) |
| Dados de um país específico | new CultureInfo("en-US") | decimal.Parse(texto, eua) |
| Comparar nomes de arquivo, extensões, códigos | Ordinal | StringComparison.OrdinalIgnoreCase |
| Ordenar nomes de pessoas para mostrar | A atual | nomes.Order() |
Quando dá errado
string[] textos = ["8.5", "7.0", "9.5"];
double soma = 0;
foreach (string t in textos)
{
soma += double.Parse(t);
}
Console.WriteLine($"Média: {soma / textos.Length}");
$ dotnet run media.cs Média: 83,33333333333333 $ LANG=en_US.UTF-8 dotnet run media.cs Média: 8.333333333333334
O que aconteceu: no computador da Ana, em pt-BR, o ponto de 8.5 é lido como separador de milhar, e as notas viram 85, 70 e 95. Num computador americano, o mesmo programa acerta. É o erro "funciona na minha máquina" clássico: não há exceção, só um resultado errado.
Como resolver: diga o formato do texto: double.Parse(t, CultureInfo.InvariantCulture), com using System.Globalization;. A média passa a ser 8,333333333333334 nos dois computadores (mostrada com vírgula aqui e com ponto lá).
FormatException: o texto no formato do outro país
A Ana esqueceu de passar a cultura americana no fornecedor.cs e trocou decimal.Parse(campos[1], eua) por decimal.Parse(campos[1]):
$ dotnet run fornecedor.cs Unhandled exception. System.FormatException: The input string '1,299.90' was not in a correct format. at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value) at System.Number.ParseDecimal[TChar](ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info) at Program.<Main>$(String[] args) in /home/ana/cs/fornecedor.cs:line 8
O que aconteceu: "O texto não está num formato correto". Em pt-BR, 1,299.90 teria a vírgula decimal antes de um ponto de milhar, o que não faz sentido. O mesmo acontece ao contrário: double.Parse("1.234,5", CultureInfo.InvariantCulture) lança The input string '1.234,5' was not in a correct format.
Como resolver: descubra de que país (ou de que programa) vem o texto e passe a cultura certa. Se o texto pode vir errado, use TryParse e avise o usuário.
double[] notas = [8.5, 7, 9.25];
Console.WriteLine(string.Join(",", notas));
$ dotnet run juntar.cs 8,5,7,9,25
O que aconteceu: o string.Join transformou cada número em texto com a cultura atual, e as vírgulas decimais se misturaram com as vírgulas que separam os itens. Quem ler essa linha vai achar cinco números: 8, 5, 7, 9 e 25.
Como resolver: transforme os números com a cultura invariante antes de juntar: string.Join(",", notas.Select(n => n.ToString(CultureInfo.InvariantCulture))) dá 8.5,7,9.25 (o Select é da aula Filtrar e Projetar). Ou use ; como separador.
Referência rápida
| Código | O que faz |
|---|---|
using System.Globalization; | Traz CultureInfo e NumberStyles |
CultureInfo.CurrentCulture | A cultura atual (no Linux, vem do LANG) |
CultureInfo.InvariantCulture | A cultura que não muda: ponto decimal, igual em todo lugar |
new CultureInfo("en-US") / CultureInfo.GetCultureInfo("en-US") | A cultura de um país |
x.ToString(cultura) / x.ToString("N2", cultura) | Número ou data → texto, com a cultura escolhida |
string.Create(cultura, $"…") | Interpolação com a cultura escolhida |
double.Parse(t, CultureInfo.InvariantCulture) | Lê "3.5" como três e meio |
double.TryParse(t, NumberStyles.Float, cultura, out double x) | A versão segura, com cultura |
CultureInfo.CurrentCulture = new CultureInfo("en-US"); | Muda a cultura do programa daqui em diante |
c.NumberFormat.NumberDecimalSeparator, CurrencySymbol | As regras de números da cultura |
StringComparison.OrdinalIgnoreCase / CurrentCultureIgnoreCase | Comparar textos por código / pelas regras da língua |
Exercícios
1. A cultura que não muda
Complete para o número sair com ponto em qualquer computador:
2. Os Estados Unidos
Complete o nome da cultura do inglês dos Estados Unidos:
3. Quanto vale?
Num computador em pt-BR, o que Console.WriteLine(double.Parse("2.5")) mostra?
4. De onde o .NET tira a cultura atual no Linux?
5. O programa grava preços num arquivo que será lido por um sistema de outra empresa. Qual cultura usar ao gravar?
6. Para comparar a extensão de um arquivo (.JPG e .jpg), qual comparação é a indicada?
Missão final: o pedido do fornecedor
Missão: o pedido.txt (na outra aba) veio de um fornecedor dos Estados Unidos, com os preços no formato de lá. Rode e veja a FormatException. Conserte criando a cultura new CultureInfo("en-US") e passando-a para o decimal.Parse. A saída deve ser:
Notebook: R$ 1.299,90 Mouse: R$ 49,90 Total: R$ 1.349,80
using System.Globalization;
decimal total = 0;
foreach (string linha in File.ReadAllLines("pedido.txt"))
{
string[] campos = linha.Split(';');
decimal preco = decimal.Parse(campos[1]);
Console.WriteLine($"{campos[0]}: {preco:C}");
total += preco;
}
Console.WriteLine($"Total: {total:C}");
Notebook;1,299.90 Mouse;49.90
using System.Globalization;
CultureInfo eua = new CultureInfo("en-US");
decimal total = 0;
foreach (string linha in File.ReadAllLines("pedido.txt"))
{
string[] campos = linha.Split(';');
decimal preco = decimal.Parse(campos[1], eua);
Console.WriteLine($"{campos[0]}: {preco:C}");
total += preco;
}
Console.WriteLine($"Total: {total:C}");
Se quiser ir além: mostre também o total no formato do fornecedor, com total.ToString("C", eua), que dá $1,349.80 (só o desenho muda; o valor continua em reais).
Resumo
- A cultura decide como números e datas viram texto e como texto vira número: separadores, moeda, formato de data, nomes de dias e meses, ordem alfabética.
- A cultura atual (
CultureInfo.CurrentCulture) vem do sistema; no Linux, da variávelLANG. No computador da Ana,pt-BR:3,5eR$ 1.234,50. - Para pessoas, use a cultura atual. Para arquivos e outros programas,
CultureInfo.InvariantCulture:x.ToString(CultureInfo.InvariantCulture)dá3.5em qualquer computador. - Em pt-BR,
double.Parse("3.5")dá 35, sem erro. Para ler texto com ponto:double.Parse("3.5", CultureInfo.InvariantCulture). - Dados de um país:
new CultureInfo("en-US")noParseou noToString. Ler com uma cultura e mostrar com outra é normal. - Textos:
Order()segue a língua;StringComparison.OrdinalIgnoreCaseé para nomes de arquivo e códigos.