C# Texto → Número

Tudo o que o usuário digita chega ao programa como texto. Mesmo que ele digite 29, o programa recebe a string "29", e com uma string não dá para fazer contas. O mesmo vale para números que vêm de um arquivo ou de um site. Nesta aula você vai aprender a transformar texto em número com Parse, ver as exceções que aparecem quando o texto não é um número, descobrir por que "3.5" vira 35 num computador em português, e usar o TryParse, o jeito seguro de ler números do teclado.

Você vai aprender
  • Por que "29" + 1 dá "291", e como converter com int.Parse, double.Parse e decimal.Parse.
  • As exceções do Parse: FormatException, OverflowException e ArgumentNullException.
  • A cultura: por que double.Parse("3.5") dá 35 no Ubuntu em português, e o CultureInfo.InvariantCulture.
  • O TryParse com out: converter sem o programa parar.
  • Ler um número do teclado com Console.ReadLine + TryParse.
  • bool.Parse, char.Parse e o Convert.ToInt32.

Todos os programas desta aula foram rodados com o .NET 10 (C# 14) na máquina fictícia da Ana (usuária ana, computador notebook, Ubuntu em português do Brasil), na pasta ~/cs, com dotnet run arquivo.cs.


A ideia: o número escrito no papel

Alguém deixa um bilhete para a Ana: "comprei 29 laranjas". O bilhete tem os desenhos 2 e 9, mas para somar com as laranjas que ela já tem, ela precisa ler o bilhete e entender que ali está o número vinte e nove.

No C#, a string "29" é o bilhete: dois caracteres, '2' e '9'. O int 29 é o número. "Ler o bilhete" se chama parse (em inglês, "analisar"): olhar o texto, conferir se ele forma um número e calcular esse número.


1. Parse: de texto para número

string texto = "29";
int idade = int.Parse(texto);
Console.WriteLine(idade + 1);
Console.WriteLine(texto + 1);
double altura = double.Parse("1,65");
decimal preco = decimal.Parse("19,90");
long grande = long.Parse("8100000000");
Console.WriteLine(altura * 2);
Console.WriteLine(preco * 2);
Console.WriteLine(grande);
$ dotnet run parse1.cs
30         « o número 29, mais 1
291        « o texto "29", com "1" grudado
3,3
39,80
8100000000
int.Parse(texto)
"Analise este texto como int". Recebe a string "29" e devolve o int 29. Cada tipo de número tem o seu: int.Parse, long.Parse, double.Parse, decimal.Parse…
idade + 1 → 30 · texto + 1 → 291
A diferença entre o número e o texto: o + soma números, mas junta textos (aula Operadores Aritméticos).
double.Parse("1,65") · decimal.Parse("19,90")
Com vírgula! O Parse lê o texto do jeito do país do computador, e no Brasil a vírgula separa os decimais. A seção 3 explica isso com calma.

Você já viu na aula Conversão de Tipos que o cast não funciona aqui: (int)"29" nem compila (CS0030). Texto e número são coisas diferentes demais; é preciso analisar o texto, e quem faz isso é o Parse.

O que o Parse aceita

Console.WriteLine(int.Parse("  42  "));
Console.WriteLine(int.Parse("-7"));
Console.WriteLine(int.Parse("+7"));
Console.WriteLine(int.Parse("007"));
$ dotnet run parseespaco.cs
42
-7
7
7

Espaços no começo e no fim são ignorados, o sinal (- ou +) é aceito, e zeros à esquerda não atrapalham. Qualquer outra coisa, não.


2. Quando o texto não é um número

Se o texto não forma um número, o Parse não tem o que devolver. Ele lança uma exceção, e o programa para:

int idade = int.Parse("vinte");
Console.WriteLine(idade);
$ dotnet run parseerro1.cs
Unhandled exception. System.FormatException: The input string 'vinte' was not in a correct format.
   at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   at System.Int32.Parse(String s)
   at Program.<Main>$(String[] args) in /home/ana/cs/parseerro1.cs:line 1
System.FormatException
"Exceção de formato": o texto não está no formato de um número.
The input string 'vinte' was not in a correct format.
"O texto de entrada 'vinte' não estava num formato correto." A mensagem mostra o texto que veio, o que ajuda muito a achar o problema.
at System.Number.ThrowFormatException… · at System.Int32.Parse(String s)
As linhas de dentro do .NET: o erro nasceu dentro do int.Parse (lembre: Int32 é o nome .NET do int).
at Program.<Main>$ … parseerro1.cs:line 1
A linha que interessa: o seu código, linha 1. O código de saída é 134.

Há três exceções possíveis:

ChamadaExceçãoMensagem
int.Parse("vinte")FormatExceptionThe input string 'vinte' was not in a correct format.
int.Parse("29.5")FormatExceptionThe input string '29.5' was not in a correct format. (um int não tem vírgula)
int.Parse("")FormatExceptionThe input string '' was not in a correct format. (o usuário só apertou Enter)
int.Parse("1.000")FormatExceptionThe input string '1.000' was not in a correct format. (separador de milhar não vale no int)
int.Parse("8100000000")OverflowExceptionValue was either too large or too small for an Int32. (é um número, mas não cabe no int)
int.Parse(null)ArgumentNullExceptionValue cannot be null. (Parameter 's') (não há texto nenhum)

Todas essas mensagens saíram do laboratório. Exceções saem sempre em inglês, mesmo no Ubuntu em português. Elas têm um capítulo inteiro, a partir de Exceções; por enquanto, o importante é: com texto que vem de fora (do teclado, de um arquivo), o Parse pode parar o seu programa. A seção 4 mostra como evitar.


3. A cultura: por que "3.5" vira 35

Agora a pegadinha mais famosa do C# no Brasil. No código, números com vírgula se escrevem com ponto (3.5). Mas o Parse lê o texto do jeito do país do computador. Veja no Ubuntu da Ana, que está em português:

double a = double.Parse("3,5");
double b = double.Parse("3.5");
double c = double.Parse("1.234,5");
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
$ dotnet run cultura1.cs
3,5
35         « "3.5" virou trinta e cinco!
1234,5

No Brasil, a vírgula separa os decimais e o ponto separa os milhares (1.234,5). Então, para o double.Parse em pt-BR, o ponto de "3.5" é um separador de milhar, e separadores de milhar são simplesmente ignorados: sobra 35. Nenhuma exceção, nenhum aviso: só um número dez vezes maior. Com dinheiro é pior:

decimal p = decimal.Parse("19.90");
Console.WriteLine(p);
decimal q = decimal.Parse("19,90");
Console.WriteLine(q);
$ dotnet run cultura3.cs
1990       « R$ 19,90 virou R$ 1.990!
19,90

Num computador configurado em inglês (en-US), seria o contrário: "19.90" daria 19,90 e "19,90" daria 1990. O mesmo programa dá resultados diferentes dependendo de onde roda. A configuração de país, idioma e formatos se chama cultura (culture), e a desta máquina é pt-BR.

A solução: dizer qual cultura o texto usa

Quando o texto vem de um lugar com formato fixo (um arquivo CSV, um site, uma API, outro programa), quase sempre ele usa o ponto, como o código. Para esses casos existe a cultura invariante, CultureInfo.InvariantCulture, que é sempre igual em qualquer computador: ponto para decimais, sem separador de milhar.

using System.Globalization;
double b = double.Parse("3.5", CultureInfo.InvariantCulture);
Console.WriteLine(b);
decimal p = decimal.Parse("19.90", CultureInfo.InvariantCulture);
Console.WriteLine(p);
Console.WriteLine(CultureInfo.CurrentCulture.Name);
$ dotnet run cultura2.cs
3,5
19,90
pt-BR
using System.Globalization;
"Globalização": a parte do .NET que cuida de culturas. O using deixa você escrever CultureInfo sem o nome completo (aula Namespaces e using).
double.Parse("3.5", CultureInfo.InvariantCulture)
O segundo valor dentro dos parênteses diz: "leia este texto no formato invariante". Agora "3.5" é três e meio.
3,5 · 19,90
Na saída, o Console.WriteLine continua usando a cultura do computador, por isso a vírgula. Ler e mostrar são coisas separadas.
CultureInfo.CurrentCulture.Name
A cultura atual: pt-BR.
De onde vem o textoComo ler
Digitado pelo usuário brasileirodouble.Parse(texto) (cultura do computador: 3,5)
Arquivo CSV, JSON, site, API, outro programadouble.Parse(texto, CultureInfo.InvariantCulture) (3.5)

A aula Cultura (pt-BR × en-US) mostra o assunto inteiro: como mostrar números em outra cultura, datas, moedas e como trocar a cultura do programa.

Missão: o preço da bala veio de um site estrangeiro como "3.50", e o programa mostra Preço: 350. Passe CultureInfo.InvariantCulture como segundo valor do decimal.Parse para mostrar Preço: 3,50.

using System.Globalization;
string doSite = "3.50";
decimal preco = decimal.Parse(doSite);
Console.WriteLine($"Preço: {preco}");
using System.Globalization;
string doSite = "3.50";
decimal preco = decimal.Parse(doSite, CultureInfo.InvariantCulture);
Console.WriteLine($"Preço: {preco}");

4. TryParse: tentar sem quebrar

O Parse é ótimo quando você tem certeza de que o texto é um número. Quando o texto vem de uma pessoa, não há certeza nenhuma: ela pode digitar "vinte", "29 anos" ou só apertar Enter. Para isso existe o TryParse ("tentar analisar"), que nunca lança exceção. Ele responde duas coisas: se deu certo e qual é o número.

string texto = "29";
if (int.TryParse(texto, out int idade))
{
    Console.WriteLine($"Idade: {idade}");
}
else
{
    Console.WriteLine("Isso não é um número.");
}
string ruim = "vinte";
bool deuCerto = int.TryParse(ruim, out int outro);
Console.WriteLine(deuCerto);
Console.WriteLine(outro);
$ dotnet run tryparse1.cs
Idade: 29
False
0
int.TryParse(texto, out int idade)
Tenta converter texto. Devolve um bool: true se deu certo, false se não. E o número vai para a variável depois do out.
out int idade
out ("saída") cria a variável idade ali mesmo e deixa o TryParse guardar o número nela. É um segundo "resultado" do método, além do bool. Os detalhes do out estão em ref, out e in.
if ( … ) { … } else { … }
O if ("se") roda o primeiro bloco quando o bool é true; o else ("senão") roda o segundo quando é false. Por enquanto, leia assim: "se conseguir converter, mostre a idade; senão, avise". O if tem aula própria: if e else.
deuCerto → False · outro → 0
Com "vinte", o TryParse devolve false, e a variável do out fica com 0. O programa não parou.

Você também pode escrever out var idade: o tipo vem do TryParse que você chamou (aula Inferência com var). E o TryParse aceita a cultura, como o Parse: double.TryParse(texto, CultureInfo.InvariantCulture, out double valor).

Boa prática: entrada do usuário é sempre TryParse

Use Parse só com texto que você controla (que o seu próprio programa gerou). Para o que vem do teclado, de arquivos ou da internet, use TryParse e diga ao usuário o que deu errado.


5. Lendo um número do teclado

Console.ReadLine() ("ler linha") espera o usuário digitar e apertar Enter, e devolve o texto digitado (aula Ler do Teclado). Junto com o TryParse, você tem o jeito certo de pedir um número:

Console.Write("Quantos cadernos? ");
string? texto = Console.ReadLine();
if (int.TryParse(texto, out int quantidade))
{
    decimal total = quantidade * 4.50m;
    Console.WriteLine($"Total: R$ {total}");
}
else
{
    Console.WriteLine($"\"{texto}\" não é um número inteiro.");
}

Rodando três vezes, digitando 3, depois três, depois 12 com espaços em volta:

$ dotnet run readline1.cs
Quantos cadernos? 3
Total: R$ 13,50
$ dotnet run readline1.cs
Quantos cadernos? três
"três" não é um número inteiro.
$ dotnet run readline1.cs
Quantos cadernos?  12
Total: R$ 54,00
string? texto = Console.ReadLine();
O ? depois de string avisa que o texto pode ser null: o ReadLine devolve null quando não há mais entrada (aula Referências Anuláveis). O TryParse aceita null sem problema: devolve false.
\"{texto}\"
\" põe uma aspa dentro do texto (aula Escapes).

O mesmo com Parse: aviso e exceção

Console.Write("Sua idade: ");
int idade = int.Parse(Console.ReadLine());
Console.WriteLine($"Ano que vem: {idade + 1}");
$ dotnet run readlineparse.cs
/home/ana/cs/readlineparse.cs(2,23): warning CS8604: Possível argumento de referência nula para o parâmetro 's' em 'int int.Parse(string s)'.
Sua idade: vinte
Unhandled exception. System.FormatException: The input string 'vinte' was not in a correct format.
   at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   at System.Int32.Parse(String s)
   at Program.<Main>$(String[] args) in /home/ana/cs/readlineparse.cs:line 2

Dois problemas. Antes de rodar, o compilador já avisa (CS8604): o ReadLine pode devolver null, e o int.Parse não aceita null (aconteceria um ArgumentNullException). Depois, quando a Ana digita vinte, o programa para com FormatException. Com TryParse, os dois problemas somem.


6. Outros tipos e o Convert

Console.WriteLine(bool.Parse("true"));
Console.WriteLine(bool.Parse(" False "));
Console.WriteLine(bool.TryParse("sim", out bool b));
Console.WriteLine(char.Parse("A"));
Console.WriteLine(double.Parse("1e3"));
Console.WriteLine(decimal.TryParse("R$ 10,00", out decimal d));
Console.WriteLine(int.TryParse("12abc", out int i));
$ dotnet run outros.cs
True
False
False
A
1000
False
False
bool.Parse("true") · bool.Parse(" False ")
Aceita true e false, com maiúsculas ou minúsculas e espaços em volta. Só em inglês: "sim" dá false no TryParse (e FormatException: String 'sim' was not recognized as a valid Boolean. no Parse).
char.Parse("A")
Uma string de exatamente um caractere vira char.
double.Parse("1e3")
A notação científica é aceita: 1000.
"R$ 10,00" · "12abc"
Símbolos e letras no meio fazem o TryParse devolver false. (Para ler moeda, existe decimal.Parse(texto, NumberStyles.Currency), que aceita "R$ 1.234,50".)

Convert.ToInt32

A classe Convert (aula Conversão de Tipos) também converte texto: Convert.ToInt32("29") é o mesmo que int.Parse("29"), com as mesmas exceções. A única diferença: com null, o Convert.ToInt32 devolve 0 em vez de lançar ArgumentNullException. Isso parece cômodo, mas esconde o problema ("o usuário digitou zero ou não digitou nada?"). Prefira TryParse.

SituaçãoUse
Texto que veio do usuário, de um arquivo, da internetint.TryParse(texto, out int n)
Texto que com certeza é um número (o seu programa gerou)int.Parse(texto)
Texto com ponto decimal, de arquivo ou APIdouble.Parse(texto, CultureInfo.InvariantCulture)
Dinheiro digitadodecimal.TryParse(texto, out decimal valor)
Sim/não em textobool.TryParse (só true/false)

Quando dá errado

System.FormatException: The input string '…' was not in a correct format.

O que significa: o texto não é um número do tipo pedido: tem letras, está vazio, tem vírgula num int, ou separador de milhar ("1.000") num int. A mensagem mostra o texto entre aspas simples.

Como consertar: use TryParse e trate o false. Se o texto devia ser válido, olhe o que está entre as aspas da mensagem: muitas vezes há um espaço ou um caractere a mais.

System.OverflowException: Value was either too large or too small for an Int32.

O que significa: o texto é um número, mas não cabe no tipo ("8100000000" num int).

Como consertar: use um tipo maior (long.Parse). O TryParse também devolve false nesse caso.

System.ArgumentNullException: Value cannot be null. (Parameter 's')

O que significa: o Parse recebeu null, por exemplo de um Console.ReadLine() quando a entrada acabou.

Como consertar: TryParse, que aceita null e devolve false.

warning CS8604: Possível argumento de referência nula para o parâmetro 's' em 'int int.Parse(string s)'.

O que significa: o compilador viu que o valor passado ao Parse pode ser null (o retorno do ReadLine é string?). É um aviso: o programa roda, mas pode parar com ArgumentNullException.

Como consertar: troque por TryParse. (Aula Avisos do Compilador.)

O número saiu 10 ou 100 vezes maior ("3.5" → 35, "19.90" → 1990)

O que significa: um texto com ponto decimal foi lido na cultura pt-BR, em que o ponto separa milhares. Não há exceção nem aviso.

Como consertar: se o texto vem de arquivo, site ou outro programa, leia com CultureInfo.InvariantCulture. Se vem de um usuário brasileiro, peça a vírgula. Veja Cultura.

error CS0030: Não é possível converter tipo "string" em "int"

O que significa: você tentou (int)texto. Cast não converte texto.

Como consertar: int.Parse(texto) ou int.TryParse(texto, out int n).


Referência rápida

EscritaO que faz
int.Parse(texto)Converte ou lança FormatException / OverflowException / ArgumentNullException
long.Parse, double.Parse, decimal.Parse, bool.Parse, char.ParseO mesmo para cada tipo
int.TryParse(texto, out int n)Devolve true/false; o número vai para n (0 se falhou)
double.Parse(texto, CultureInfo.InvariantCulture)Lê com ponto decimal, em qualquer computador
double.TryParse(texto, CultureInfo.InvariantCulture, out double d)TryParse com cultura
decimal.Parse(texto, NumberStyles.Currency)Aceita símbolo de moeda e milhar ("R$ 1.234,50")
Convert.ToInt32(texto)Como int.Parse, mas null vira 0
numero.ToString()O caminho contrário: número para texto

Exercícios

1. O método

Complete para converter o texto em int:

int n = int.("42");

2. A palavra do TryParse

Complete a chamada:

bool ok = int.TryParse(texto, int numero);

3. A cultura

No Ubuntu da Ana (pt-BR), quanto vale double.Parse("2.5")?

4. O que int.Parse("12,5") faz?

5. int.TryParse("abc", out int n) devolve o quê, e quanto vale n?

6. Um arquivo CSV de um sistema americano tem o valor "1250.75". Como ler em decimal num computador em pt-BR?

7. Por que usar TryParse com o que o usuário digita?

Missão final: o pedido que não quebra

Missão: o caixa pergunta quantos cadernos a cliente quer, e ela digitou doze (por extenso). Com int.Parse, o programa para com FormatException. Reescreva usando int.TryParse, como no exemplo da seção 5: se der certo, mostre o total; senão, mostre Quantidade inválida: doze (o texto digitado), e o programa termina normalmente.

Console.Write("Quantos cadernos? ");
string? texto = Console.ReadLine();
int quantidade = int.Parse(texto);
Console.WriteLine($"Total: R$ {quantidade * 4.50m}");
Console.Write("Quantos cadernos? ");
string? texto = Console.ReadLine();
if (int.TryParse(texto, out int quantidade))
{
    Console.WriteLine($"Total: R$ {quantidade * 4.50m}");
}
else
{
    Console.WriteLine($"Quantidade inválida: {texto}");
}

A caixa já entrega doze como se a cliente tivesse digitado. Depois de cumprir a missão, troque o programa para receber um número de verdade e veja o total aparecer.


Resumo

  • Texto não é número: "29" + 1 é "291". Para converter, int.Parse, double.Parse, decimal.Parse… (o cast não serve: CS0030).
  • Quando o texto não é um número válido, o Parse lança FormatException; se não cabe, OverflowException; se é null, ArgumentNullException.
  • O Parse usa a cultura do computador. Em pt-BR, double.Parse("3.5") dá 35 e decimal.Parse("19.90") dá 1990, sem erro. Para textos com ponto (arquivos, sites), use CultureInfo.InvariantCulture.
  • int.TryParse(texto, out int n) devolve true/false e nunca lança exceção. Use-o sempre com o que o usuário digita.
  • Console.ReadLine() devolve string?; o TryParse aceita null, o Parse não (aviso CS8604).