C# Strings
Quase todo programa mexe com texto: o nome do cliente, a mensagem na tela, a senha, a lista de compras. Em C#, um texto é uma string (em inglês, "corrente", "fileira": uma fileira de letras). Você já usou strings desde o "Olá, mundo". Nesta aula você vai ver o que uma string é por dentro, como medir e pegar letras dela, como juntar textos, por que uma string nunca muda e como comparar textos do jeito certo, inclusive ignorando maiúsculas.
- Criar strings, medir o tamanho com
Lengthe pegar uma letra comnome[0]. - Juntar textos com
+e+=. - Por que a string é imutável e o que isso muda no seu código (
nome = nome.ToUpper()). - Comparar:
==compara o conteúdo;string.EqualscomStringComparisonignora maiúsculas;string.Comparediz a ordem alfabética. - Maiúsculas e minúsculas em português (
"ação".ToUpper()), string vazia ×nulle os erros mais comuns.
Todos os programas desta aula foram rodados com o .NET 10 na máquina fictícia da Ana (usuária ana, computador notebook, sistema em português do Brasil), na pasta ~/cs, com dotnet run arquivo.cs.
A ideia: um colar de letras
Imagine um colar de contas, cada conta com uma letra pintada. O colar "Ana" tem três contas: A, n e a. Dá para contar as contas, olhar a terceira, ou fazer um colar novo juntando dois colares. O que não dá é repintar uma conta: o colar vem lacrado de fábrica. Para mudar uma letra, você faz um colar novo.
Uma string é exatamente isso: uma sequência de caracteres (char, aula char e bool), numerados a partir do zero, que não pode ser alterada depois de criada.
| Posição | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|---|
| Letra | A | n | a | (espaço) | S | o | u | z | a |
"Ana Souza": 9 caracteres, nas posições 0 a 8. O espaço também é um caractere.1. Criar uma string e medir o tamanho
string nome = "Ana";
string cidade = "São Paulo";
string vazio = "";
Console.WriteLine(nome);
Console.WriteLine(nome.Length);
Console.WriteLine(cidade.Length);
Console.WriteLine(vazio.Length);
Console.WriteLine(" ".Length);
$ dotnet run criar.cs Ana 3 « A, n, a 9 « S, ã, o, espaço, P, a, u, l, o: o ã conta como um, e o espaço também 0 « a string vazia não tem nenhum caractere 1 « um espaço é um caractere
- string nome = "Ana";
- Cria uma variável do tipo
stringcom o textoAna. O texto vai entre aspas duplas; as aspas não fazem parte dele. (Aspas simples,'A', são para umchar, uma letra só.) - ""
- A string vazia: um texto sem nenhum caractere. Existe, só não tem nada dentro, como um colar sem contas.
- nome.Length
- "Comprimento": quantos caracteres a string tem. É uma propriedade, então vem sem parênteses (
Length, nãoLength()) e comLmaiúsculo.
string com letra minúscula é o apelido que o C# dá ao tipo System.String do .NET. String nome = "Ana"; também funciona e é exatamente o mesmo tipo; o costume é escrever string, minúsculo, como int e bool.
2. Cada letra tem uma posição
Para pegar um caractere, escreva a posição entre colchetes depois da variável. A contagem começa em zero:
string nome = "Ana Souza"; Console.WriteLine(nome[0]); Console.WriteLine(nome[4]); Console.WriteLine(nome[nome.Length - 1]); Console.WriteLine(nome[^1]); char primeira = nome[0]; Console.WriteLine(primeira);
$ dotnet run indice.cs A « a posição 0 é a primeira letra S « posição 4 (contando o espaço na posição 3) a « a última: posição Length - 1 = 8 a « ^1 = "a primeira a partir do fim" A
- nome[0]
- O caractere na posição 0, o primeiro. O resultado é um
char, não umastring: por isso dá para guardar emchar primeira. - nome[nome.Length - 1]
- A última posição é sempre o tamanho menos 1: "Ana Souza" tem 9 caracteres, nas posições 0 a 8. Pedir
nome[9]derruba o programa (veja Quando dá errado). - nome[^1] C# 8
- O
^conta de trás para a frente:^1é o último,^2o penúltimo. É o mesmo quenome[nome.Length - 1], mais curto (aula Índices e Intervalos).
Experimente: troque o nome pelo seu e veja o tamanho e as letras mudarem. Tente acentos ("José"), espaços no fim ("Ana ") e a string vazia "". O que acontece com nome[0] quando o nome é vazio?
string nome = "Beatriz";
Console.WriteLine("Nome: " + nome);
Console.WriteLine("Tamanho: " + nome.Length);
Console.WriteLine("Primeira letra: " + nome[0]);
Console.WriteLine("Última letra: " + nome[^1]);
3. Juntar textos: + e +=
Juntar strings se chama concatenar ("encadear"). O + entre dois textos cola um no fim do outro:
string nome = "Ana"; string sobrenome = "Souza"; string completo = nome + " " + sobrenome; Console.WriteLine(completo); int idade = 29; Console.WriteLine(nome + " tem " + idade + " anos"); string lista = "arroz"; lista += ", feijão"; lista += ", café"; Console.WriteLine(lista);
$ dotnet run juntar.cs Ana Souza Ana tem 29 anos arroz, feijão, café
- nome + " " + sobrenome
- Três pedaços colados:
"Ana", um espaço e"Souza". O+não põe espaço sozinho; se você esquecer o" ", saiAnaSouza. - " tem " + idade
- Quando um lado é texto e o outro é número, o número vira texto e é colado. Cuidado com a ordem:
"Total: " + 2 + 3dáTotal: 23(aula Precedência). - lista += ", feijão";
- "Cole isto no fim de
lista": é o mesmo quelista = lista + ", feijão";(aula Operadores de Atribuição).
Com muitos pedaços, o + fica difícil de ler. A interpolação, $"{nome} tem {idade} anos", é o jeito moderno, e tem uma aula só para ela: Interpolação.
4. Strings são imutáveis
Lembra do colar lacrado? Uma string, depois de criada, nunca muda. Os métodos que "mudam" um texto, como ToUpper() ("para maiúsculas"), na verdade criam uma string nova e devolvem essa nova. A original fica igual:
string nome = "ana"; nome.ToUpper(); Console.WriteLine(nome); string maiusculo = nome.ToUpper(); Console.WriteLine(maiusculo); Console.WriteLine(nome); nome = nome.ToUpper(); Console.WriteLine(nome);
$ dotnet run imutavel.cs ana « a linha 2 criou "ANA" e jogou fora: ninguém guardou ANA « agora a string nova foi guardada em maiusculo ana « a original continua igual ANA « nome passou a apontar para a string nova
- Linha 2:
nome.ToUpper()criou a string"ANA", mas o resultado não foi guardado em lugar nenhum. O compilador não avisa, e a linha não serve para nada. - Linha 4: o resultado foi guardado numa variável nova.
nomecontinua"ana". - Linha 7:
nome = nome.ToUpper();guarda a string nova na própria variável. A string antiga,"ana", não foi alterada: a variável é que passou a guardar outra string. É assim que se "muda" um texto em C#.
Isso também explica por que mudar uma variável não afeta a outra:
string a = "Oi"; string b = a; a += ", tudo bem?"; Console.WriteLine(a); Console.WriteLine(b);
$ dotnet run copia.cs Oi, tudo bem? Oi « b continua com a string antiga
O a += … criou uma string nova e guardou em a. A string "Oi", que b guarda, nunca foi tocada. E tentar trocar uma letra diretamente, com nome[0] = 'A', é recusado pelo compilador (erro CS0200).
Porque é seguro: uma string que você passou para outra parte do programa nunca vai mudar "por baixo" de você. O preço é que cada += cria uma string nova. Para juntar milhares de pedaços num laço, existe o StringBuilder, que é mutável (aula StringBuilder).
Missão: o crachá do evento deve mostrar o nome em maiúsculas, mas sai Crachá: ana souza. A linha do ToUpper está lá, só que o resultado se perde. Conserte para aparecer Crachá: ANA SOUZA, sem mudar o texto "ana souza".
Crachá: ANA SOUZA
string nome = "ana souza";
nome.ToUpper();
Console.WriteLine("Crachá: " + nome);
string nome = "ana souza";
nome = nome.ToUpper();
Console.WriteLine("Crachá: " + nome);
5. Comparar textos
== compara o conteúdo
Em C#, == com strings compara o texto, letra por letra. Não importa como cada string foi criada:
string senha = "gato123";
string digitada = "gato" + "123";
Console.WriteLine(senha == digitada);
Console.WriteLine(senha == "Gato123");
Console.WriteLine(senha.Equals(digitada));
Console.WriteLine(string.Equals(senha, "GATO123", StringComparison.OrdinalIgnoreCase));
Console.WriteLine(string.Equals(senha, "GATO123", StringComparison.CurrentCultureIgnoreCase));
Console.WriteLine("Recife".Equals("RECIFE", StringComparison.OrdinalIgnoreCase));
$ dotnet run igual.cs True « mesmo texto, mesmo que montado com + False « G maiúsculo é outra letra True « Equals sem opções faz o mesmo que == True « ignorando maiúsculas e minúsculas True True
- senha == digitada
- Compara o conteúdo. Quem vem do Java estranha: lá,
==com texto é uma armadilha. Em C#, é o jeito normal. - senha == "Gato123"
- Maiúsculas e minúsculas contam:
geGsão caracteres diferentes. - string.Equals(a, b, StringComparison.OrdinalIgnoreCase)
- Compara
aebignorando maiúsculas. O terceiro valor, do tipoStringComparison("comparação de strings"), diz como comparar. Também pode ser escrito comoa.Equals(b, StringComparison.OrdinalIgnoreCase).
As opções de StringComparison
| Opção | Como compara | Use para |
|---|---|---|
Ordinal | Código por código (é o que o == faz) | senhas, códigos, nomes de arquivo, comandos |
OrdinalIgnoreCase | Código por código, ignorando maiúsculas | respostas do usuário ("sim", "SIM"), e-mails, comandos |
CurrentCulture | Pelas regras do idioma do computador (aqui, pt-BR) | ordenar e comparar textos que pessoas vão ler |
CurrentCultureIgnoreCase | Idem, ignorando maiúsculas | busca por nome numa lista mostrada ao usuário |
InvariantCulture, InvariantCultureIgnoreCase | Regras fixas, iguais em qualquer computador | dados gravados em arquivo que precisam dar o mesmo resultado em qualquer lugar |
Qual usar? Na dúvida, Ordinal ou OrdinalIgnoreCase: são rápidos e dão sempre o mesmo resultado, em qualquer computador. Use as opções de cultura quando a regra do idioma importa, como na ordem alfabética de uma lista de nomes. A aula Cultura mostra as diferenças em detalhe.
Ordem alfabética: string.Compare
Como você viu em Operadores de Comparação, < e > não funcionam com strings. Para saber quem vem antes, use string.Compare(a, b). Ele devolve um número: negativo se a vem antes, zero se são iguais, positivo se a vem depois.
Console.WriteLine(string.Compare("ana", "Bia"));
Console.WriteLine(string.Compare("Bia", "ana"));
Console.WriteLine(string.Compare("ana", "ana"));
Console.WriteLine(string.Compare("ana", "Bia", StringComparison.Ordinal));
Console.WriteLine(string.Compare("é", "f"));
Console.WriteLine(string.Compare("é", "f", StringComparison.Ordinal));
$ dotnet run ordem.cs -1 « "ana" vem antes de "Bia" no dicionário 1 « "Bia" vem depois de "ana" 0 « iguais 31 « Ordinal: 'a' (97) vem DEPOIS de 'B' (66) na tabela de códigos -1 « no dicionário, "é" vem antes de "f" 131 « Ordinal: 'é' (233) vem muito depois de 'f' (102)
Sem a opção, string.Compare usa as regras do português: maiúsculas e acentos ficam no lugar que você espera num dicionário. Com Ordinal, vale o número de cada caractere, e aí todas as maiúsculas vêm antes das minúsculas e as letras acentuadas vão para o fim. Para ordenar nomes que a pessoa vai ver, não use Ordinal. Não conte com o valor exato (31, 131): só o sinal (negativo, zero, positivo) importa.
6. Maiúsculas e minúsculas em português
ToUpper() ("para cima") e ToLower() ("para baixo") conhecem os acentos:
string palavra = "ação";
Console.WriteLine(palavra.ToUpper());
Console.WriteLine("SÃO JOÃO".ToLower());
Console.WriteLine("istanbul".ToUpper());
Console.WriteLine("istanbul".ToUpper(new System.Globalization.CultureInfo("tr-TR")));
Console.WriteLine("istanbul".ToUpperInvariant());
Console.WriteLine(System.Globalization.CultureInfo.CurrentCulture.Name);
$ dotnet run upper.cs AÇÃO « ç vira Ç, ã vira à são joão ISTANBUL « pelas regras do português İSTANBUL « pelas regras do turco: o i minúsculo vira İ, com ponto! ISTANBUL « Invariant: regras fixas, iguais em qualquer computador pt-BR « a cultura da máquina da Ana
- ToUpper() e ToLower()
- Sem nada entre os parênteses, usam a cultura atual do computador (aqui,
pt-BR). Para textos em português, o resultado é o que você espera. - O caso do turco
- Em turco, existem dois "i": com ponto (i, İ) e sem ponto (ı, I). Num computador em turco,
"istanbul".ToUpper()dáİSTANBUL, e uma comparação comocomando.ToUpper() == "ISTANBUL"falha. Por isso, para comparar códigos e comandos, prefirastring.Equals(…, StringComparison.OrdinalIgnoreCase)em vez de converter para maiúsculas. - ToUpperInvariant()
- Usa regras fixas, que não dependem do idioma do computador. Bom para códigos (
"br".ToUpperInvariant()→BR).
7. String vazia × null
Uma string vazia ("") é um colar sem contas: ele existe. null é não ter colar nenhum: a variável não aponta para string alguma (aula null). Para guardar null, o tipo leva um ?: string? ("string ou nada", aula Referências Anuláveis).
string? apelido = null;
string vazio = "";
Console.WriteLine(string.IsNullOrEmpty(apelido));
Console.WriteLine(string.IsNullOrEmpty(vazio));
Console.WriteLine(string.IsNullOrWhiteSpace(" "));
Console.WriteLine(apelido == null);
Console.WriteLine(vazio == string.Empty);
Console.WriteLine(apelido.Length);
$ dotnet run nulo.cs /home/ana/cs/nulo.cs(8,19): warning CS8602: Desreferência de uma referência possivelmente nula. True True True « só espaços: "em branco" True True « string.Empty é o mesmo que "" Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object. at Program.<Main>$(String[] args) in /home/ana/cs/nulo.cs:line 8
- string.IsNullOrEmpty(texto)
- "É nulo ou vazio?" Responde
trueparanulle para"". É o teste mais usado antes de mexer num texto que veio de fora. - string.IsNullOrWhiteSpace(texto)
- "É nulo, vazio ou só espaços?" Ideal para conferir se o usuário digitou alguma coisa (aula Ler do Teclado).
- apelido.Length
- Pedir o tamanho de "colar nenhum" não faz sentido: o programa termina com
NullReferenceException("referência de objeto não definida para uma instância de um objeto"), código de saída 134. O compilador avisou antes, com oCS8602: "desreferência de uma referência possivelmente nula", isto é, "você está usando algo que pode sernull".
Formas diferentes de fazer a mesma coisa
| Formas | Qual usar |
|---|---|
string × String | string. São o mesmo tipo; o minúsculo é o costume. |
"" × string.Empty | Tanto faz: são iguais. Muitos times preferem string.Empty porque deixa claro que o vazio é de propósito. |
a == b × a.Equals(b) | ==. E a.Equals(b) quebra se a for null; == não. |
a.ToUpper() == b.ToUpper() × string.Equals(a, b, StringComparison.OrdinalIgnoreCase) | O string.Equals: não cria strings novas, não depende do idioma e aceita null. |
nome[nome.Length - 1] × nome[^1] | nome[^1] C# 8, mais curto. Em código antigo você vai ver a outra. |
a + " " + b × $"{a} {b}" | A interpolação, quando há mais de dois pedaços (aula Interpolação). |
Quando dá errado
error CS0200: A propriedade ou o indexador "string.this[int]" não pode ser atribuído, pois é somente leitura
string nome = "ana"; nome[0] = 'A'; Console.WriteLine(nome);
$ dotnet run es1.cs /home/ana/cs/es1.cs(2,1): error CS0200: A propriedade ou o indexador "string.this[int]" não pode ser atribuído, pois é somente leitura Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: as posições de uma string são "somente leitura": dá para olhar nome[0], mas não trocar. É a imutabilidade.
Como consertar: crie uma string nova: nome = char.ToUpper(nome[0]) + nome[1..]; (o [1..], "da posição 1 até o fim", aparece em Métodos de String).
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
string nome = "Ana"; Console.WriteLine(nome[3]);
$ dotnet run es2.cs Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array. at System.String.get_Chars(Int32 index) at Program.<Main>$(String[] args) in /home/ana/cs/es2.cs:line 2
O que significa: "o índice estava fora dos limites". "Ana" tem as posições 0, 1 e 2; a 3 não existe. A linha at System.String.get_Chars mostra onde o .NET percebeu (dentro da string); a de baixo mostra a sua linha, a 2. O código de saída é 134.
Como consertar: a última posição é nome.Length - 1 (ou nome[^1]). Se o texto pode ser vazio, confira antes: nome.Length > 0.
error CS1012: Número excessivo de caracteres no literal de caractere
char letra = 'Ana';
$ dotnet run es3.cs /home/ana/cs/es3.cs(1,14): error CS1012: Número excessivo de caracteres no literal de caractere Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: aspas simples são para um caractere só (char). Para um texto, aspas duplas.
Como consertar: string nome = "Ana"; (ou char letra = 'A';).
error CS0019: O operador "==" não pode ser aplicado a operandos dos tipos "string" e "char"
string inicial = "A"; Console.WriteLine(inicial == 'A');
$ dotnet run es4.cs /home/ana/cs/es4.cs(2,19): error CS0019: O operador "==" não pode ser aplicado a operandos dos tipos "string" e "char" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: "A" (uma string de um caractere) e 'A' (um char) são tipos diferentes.
Como consertar: compare string com string (inicial == "A") ou char com char (inicial[0] == 'A').
error CS1061: ‘string’ não contém uma definição para "length"…
string nome = "Ana"; Console.WriteLine(nome.length);
$ dotnet run es5.cs /home/ana/cs/es5.cs(2,24): error CS1061: ‘string’ não contém uma definição para "length" e não foi possível encontrar nenhum método de extensão "length" que aceite um primeiro argumento do tipo ‘string’ (você está se esquecendo de usar uma diretiva ou uma referência de assembly?) Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que significa: o C# diferencia maiúsculas de minúsculas também nos nomes: length não existe, Length existe. A mensagem longa só quer dizer "não achei nada com esse nome".
Como consertar: nome.Length. Os nomes do .NET começam com maiúscula: Length, ToUpper, WriteLine.
NullReferenceException ao usar um texto
O que significa: a variável era null e você pediu .Length, .ToUpper() ou outro membro. O aviso CS8602 costuma aparecer antes, ao compilar.
Como consertar: confira antes com string.IsNullOrEmpty(texto), ou dê um valor padrão (texto ?? "", aula Operadores ??, ?. e ??=).
ToUpper() (ou Trim(), Replace()…)
O que significa: você chamou o método e não guardou o resultado. Strings são imutáveis: o método devolve uma string nova.
Como consertar: nome = nome.ToUpper();.
Referência
| Código | O que faz | Exemplo → resultado |
|---|---|---|
"texto" | cria uma string | string s = "Ana"; |
"" ou string.Empty | string vazia | "".Length → 0 |
s.Length | quantidade de caracteres | "Ana".Length → 3 |
s[i] | caractere na posição i (um char) | "Ana"[0] → 'A' |
s[^1] | último caractere | "Ana"[^1] → 'a' |
a + b, s += x | junta textos (cria uma string nova) | "Oi" + "!" → "Oi!" |
a == b, a != b | compara o conteúdo | "Ana" == "ana" → false |
string.Equals(a, b, StringComparison.OrdinalIgnoreCase) | compara ignorando maiúsculas | "Ana" e "ANA" → true |
string.Compare(a, b) | ordem alfabética: < 0, 0 ou > 0 | ("ana", "Bia") → negativo |
s.ToUpper(), s.ToLower() | nova string em maiúsculas / minúsculas (cultura atual) | "ação".ToUpper() → "AÇÃO" |
s.ToUpperInvariant() | maiúsculas com regras fixas | "br" → "BR" |
string.IsNullOrEmpty(s) | null ou ""? | ("") → true |
string.IsNullOrWhiteSpace(s) | null, "" ou só espaços? | (" ") → true |
Muitos outros métodos (Trim, Contains, Replace, Split, Substring…) estão na próxima aula, Métodos de String.
Exercícios
1. Tamanho
Complete para mostrar quantos caracteres tem cidade:
2. A primeira letra
Complete para pegar o primeiro caractere de nome:
3. Guardar o resultado
Complete para que nome fique em minúsculas:
4. Quanto vale "Rio de Janeiro".Length?
5. Depois de string s = "oi"; s.ToUpper(); Console.WriteLine(s);, o que aparece?
6. Qual comparação dá true para "sim" e "SIM"?
7. string.Compare("Bia", "ana") dá um número positivo. O que isso quer dizer?
Missão final: o jogo de perguntas
Missão: num jogo de perguntas, a cidade certa é Recife e a jogadora respondeu RECIFE. Conserte a comparação para ignorar maiúsculas (use string.Equals com StringComparison), e troque o 0 da última linha pelo tamanho da palavra, calculado com Length (não escreva o 6 à mão).
Acertou? True A palavra tem 6 letras
string cidade = "Recife";
string resposta = "RECIFE";
bool acertou = resposta == cidade;
Console.WriteLine("Acertou? " + acertou);
Console.WriteLine("A palavra tem " + 0 + " letras");
string cidade = "Recife";
string resposta = "RECIFE";
bool acertou = string.Equals(resposta, cidade, StringComparison.OrdinalIgnoreCase);
Console.WriteLine("Acertou? " + acertou);
Console.WriteLine("A palavra tem " + cidade.Length + " letras");
Resumo
- Uma
stringé uma sequência dechar, entre aspas duplas.Length(sem parênteses) dá o tamanho. - As posições começam em 0:
s[0]é o primeiro,s[s.Length - 1]ous[^1]o último. Fora disso,IndexOutOfRangeException. +e+=juntam textos, criando strings novas.- Strings são imutáveis: métodos devolvem uma string nova. Guarde o resultado:
nome = nome.ToUpper();.s[0] = 'A'dáCS0200. ==compara o conteúdo. Para ignorar maiúsculas:string.Equals(a, b, StringComparison.OrdinalIgnoreCase).string.Compare(a, b)diz a ordem: negativo, zero ou positivo. Sem opção, segue o português; comOrdinal, os códigos.ToUpper()segue a cultura ("ação"→"AÇÃO"); para códigos,ToUpperInvariant().""é vazio;nullé nada. Teste comstring.IsNullOrEmptyouIsNullOrWhiteSpace.