C++ Caracteres Especiais e Raw Strings
Como escrever, dentro de um texto entre aspas, uma aspa? Ou um Tab, que você não consegue "ver"? Ou o caminho C:\Users\ana, cheio de barras invertidas? Desde a aula Novas Linhas você usa o \n para pular linha. Ele é o mais famoso de uma família: as sequências de escape. Nesta aula você vai conhecer a família inteira, o erro clássico do caminho do Windows, e as raw strings ("strings cruas"), que desligam os escapes de uma vez.
- O que é uma sequência de escape e as mais usadas:
\n,\t,\\,\",\'e\0. - Por que
"C:\novos"pula linha e o que é o avisounknown escape sequence. - Acentos e símbolos no código (
ç,€) e os escapes\ue\x. - Raw strings
R"(…)"para caminhos do Windows, expressões regulares e textos com aspas. - O delimitador
R"x(…)x", para quando o texto contém)". - Textos longos em várias linhas, com raw strings e com literais lado a lado
"a" "b".
Todos os programas desta aula foram compilados com o g++ 15.2 na máquina fictícia da Ana, na pasta ~/cpp, com o comando padrão g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa.
A ideia: a barra invertida "avisa" o compilador
Dentro de um texto entre aspas, alguns caracteres têm um papel para o compilador: a aspa " diz "o texto acabou". Outros caracteres não têm como ser digitados de forma visível, como a quebra de linha (se você apertar Enter no meio das aspas, o texto fica "aberto" e dá erro).
A solução é um código de duas letras que começa com a barra invertida \ (backslash). A barra é um aviso: "o próximo caractere não é ele mesmo; é um código". O nome técnico é escapar o caractere, tirá-lo do seu papel normal. Para o compilador, \n é um caractere só (a quebra de linha), embora você digite dois.
1. As sequências de escape mais usadas
#include <iostream>
#include <string>
int main() {
std::cout << "Linha 1\nLinha 2\n";
std::cout << "Nome\tIdade\n";
std::cout << "Ana\t30\n";
std::cout << "Ela disse: \"oi\"\n";
std::cout << "copo d'água e copo d\'água\n";
std::cout << "C:\\Users\\ana\n";
std::cout << '\'' << ' ' << '"' << '\n';
}
$ g++ -std=c++23 -Wall -Wextra escapes.cpp -o escapes $ ./escapes Linha 1 Linha 2 Nome Idade Ana 30 Ela disse: "oi" copo d'água e copo d'água C:\Users\ana ' "
| Escape | Vira | Nome em inglês | Quando usar |
|---|---|---|---|
\n | Quebra de linha | new line | Pular linha (aula Novas Linhas) |
\t | Tab (tabulação) | tab | Alinhar colunas simples. O terminal pula até a próxima "parada", de 8 em 8 colunas. |
\" | " | double quote | Uma aspa dupla dentro de "…" |
\' | ' | single quote | Obrigatório num char: '\''. Dentro de "…", opcional: "d'água" funciona. |
\\ | \ | backslash | Uma barra invertida de verdade (caminhos do Windows) |
\0 | Caractere nulo | null | Marca o fim dos textos da linguagem C (seção 3) |
\r | Volta ao começo da linha | carriage return | Arquivos do Windows terminam linhas com \r\n; barras de progresso no terminal |
\a | "Bip" do terminal | alert | Raramente; muitos terminais nem tocam |
Repare na última linha do programa: num char (aspas simples), quem precisa de escape é a aspa simples ('\''); a dupla vai solta ('"'). Num texto (aspas duplas), é o contrário. A regra: só precisa escapar a aspa igual à que está "abraçando".
Missão: faça o programa mostrar exatamente Arquivo salvo em "C:\Dados\notas.txt", com as aspas e as barras. Use \" e \\. (Rode primeiro como está e leia o aviso do compilador.)
#include <iostream>
int main() {
std::cout << "Arquivo salvo em C:\Dados\notas.txt\n";
}
#include <iostream>
int main() {
std::cout << "Arquivo salvo em \"C:\\Dados\\notas.txt\"\n";
}
2. A armadilha do caminho do Windows
O Windows separa as pastas com barra invertida. Copie um caminho para dentro de aspas e veja:
#include <iostream>
#include <string>
int main() {
std::string pasta = "C:\novos\testes";
std::cout << pasta << '\n';
std::string outra = "C:\pasta";
std::cout << outra << '\n';
}
$ g++ -std=c++23 -Wall -Wextra caminho.cpp -o caminho
caminho.cpp: In function ‘int main()’:
caminho.cpp:7:25: warning: unknown escape sequence: ‘\p’
7 | std::string outra = "C:\pasta";
| ^~~~~~~~~~
$ ./caminho
C:
ovos estes « \n pulou linha e \t virou um Tab!
C:pasta « o \p "sumiu" com a barra
- "C:\novos\testes"
- O compilador leu
\n(quebra de linha) +ovos+\t(Tab) +estes. Sem nenhum aviso, porque\ne\tsão escapes válidos: para ele, você quis isso mesmo. - warning: unknown escape sequence: ‘\p’
- "Sequência de escape desconhecida:
\p". Não existe escape\p; o g++ avisa e usa só op, jogando a barra fora.
Três formas de consertar:
| Forma | Exemplo | Comentário |
|---|---|---|
| Dobrar as barras | "C:\\novos\\testes" | Funciona sempre; fica difícil de ler com muitas barras. |
| Raw string | R"(C:\novos\testes)" | O texto fica exatamente como no Windows. Seção 5. |
| Barra normal | "C:/novos/testes" | O próprio Windows aceita / na maioria dos programas, e a biblioteca de arquivos do C++ também. |
3. O caractere nulo \0
Você viu em Strings que um texto entre aspas termina com um '\0' escondido. Por isso, se você escrever um \0 no meio de um literal, quem lê o literal acha que o texto acabou ali:
#include <iostream>
#include <string>
int main() {
std::string a = "abc\0def";
std::cout << a << " tem " << a.size() << '\n';
std::string b("abc\0def", 7);
std::cout << b << " tem " << b.size() << '\n';
}
$ g++ -std=c++23 -Wall -Wextra nulo.cpp -o nulo $ ./nulo | od -c 0000000 a b c t e m 3 \n a b c \0 d e 0000020 f t e m 7 \n 0000030
(O od -c mostra cada byte da saída, inclusive os invisíveis, como o \0 e o \n.) A string a ficou só com abc: ao copiar o literal, o std::string parou no primeiro \0. A b, criada dizendo "copie 7 caracteres", guardou o \0 no meio. Um std::string pode conter \0; os textos da linguagem C, não. Na prática, você quase nunca vai escrever \0; o importante é reconhecê-lo.
4. Acentos e símbolos no código
O código-fonte do guia é salvo em UTF-8, o formato de texto do Linux, do macOS e da web. Por isso você pode escrever ç, ã, € e até emojis direto entre as aspas. Também dá para escrever qualquer caractere pelo seu código:
#include <iostream>
#include <string>
int main() {
std::string a = "maçã";
std::string b = "ma\u00e7\u00e3";
std::cout << a << ' ' << b << ' ' << (a == b) << '\n';
std::cout << "\x41\x6e\x61" << '\n';
std::cout << "Preço: R$ 10\u20ac? Não, \u2764\n";
}
$ g++ -std=c++23 -Wall -Wextra unicode.cpp -o unicode $ ./unicode maçã maçã 1 Ana Preço: R$ 10€? Não, ❤
- \u00e7
- O caractere de código Unicode
00e7(em hexadecimal): oç. Unicode é a grande tabela que dá um número a cada caractere de todas as línguas.\urecebe 4 dígitos hexadecimais; para códigos maiores,\Ucom 8. - (a == b)
1: escreverçou\u00e7dá exatamente o mesmo texto.- \x41
- Um byte pelo seu valor em hexadecimal:
41é oAna tabela ASCII (veja Caracteres).
Na prática, escreva os acentos direto: é mais legível. O \u é útil para caracteres invisíveis ou difíceis de digitar. Atenção: escrever acentos é fácil; medir e mexer em textos com acento tem surpresas ("maçã" tem 6 bytes). Isso é o assunto da próxima aula, Acentos e UTF-8.
5. Raw strings: R"(…)"
Quando o texto tem muitas barras ou aspas, escapar uma por uma vira uma sopa de \\ e \". A raw string C++11 ("string crua") desliga os escapes: tudo entre R"( e )" é texto, exatamente como está.
#include <iostream>
#include <string>
int main() {
std::string pasta = R"(C:\Users\ana\novos\testes)";
std::string fala = R"(Ela disse: "oi" e foi embora)";
std::string padrao = R"(\d{3}\.\d{3}\.\d{3}-\d{2})";
std::cout << pasta << '\n' << fala << '\n' << padrao << '\n';
}
$ g++ -std=c++23 -Wall -Wextra raw.cpp -o raw
$ ./raw
C:\Users\ana\novos\testes
Ela disse: "oi" e foi embora
\d{3}\.\d{3}\.\d{3}-\d{2}
- R
- De raw, "cru". Vem colado na aspa, sem espaço.
- "(
- Abre o texto cru. Os parênteses não fazem parte do texto: são as "bordas".
- C:\Users\ana\novos\testes
- O conteúdo. Nenhuma barra é escape:
\naqui são dois caracteres,\en. - )"
- Fecha. O texto só acaba quando aparece
)"juntos, então aspas soltas no meio ("oi") não atrapalham.
Os três usos clássicos estão no exemplo: caminhos do Windows, textos com aspas (como um pedaço de JSON ou de HTML) e expressões regulares. Uma expressão regular (ou regex) é um "molde" para procurar padrões em textos; a do exemplo é o molde de um CPF (\d = um dígito, {3} = três vezes, \. = um ponto). Regex usa muita barra invertida, e sem raw string cada \d teria de ser escrito \\d.
Textos em várias linhas
A raw string também aceita Enter de verdade: as quebras de linha que você digitar fazem parte do texto. É ótima para menus, ajudas e desenhos:
#include <iostream>
#include <string>
int main() {
std::string menu = R"(=== LOJINHA DA ANA ===
1) Ver produtos
2) Comprar
3) Sair
)";
std::cout << menu;
std::cout << R"(Aqui \n não pula linha)" << '\n';
}
$ g++ -std=c++23 -Wall -Wextra rawlinhas.cpp -o rawlinhas $ ./rawlinhas === LOJINHA DA ANA === 1) Ver produtos 2) Comprar 3) Sair Aqui \n não pula linha
Numa raw string em várias linhas, os espaços no começo de cada linha também entram no texto. Por isso o menu do exemplo está "encostado" na margem do arquivo. Se você recuar as linhas para o código ficar bonito, o recuo aparece na tela.
6. Quando o texto tem )": o delimitador
A raw string acaba no primeiro )". E se o próprio texto tiver )"?
#include <iostream>
#include <string>
int main() {
std::string s = R"(Ela escreveu "(sorriso)" no fim)";
std::cout << s << '\n';
}
$ g++ -std=c++23 -Wall -Wextra rawerro.cpp -o rawerro
rawerro.cpp:5:56: warning: missing terminating " character
5 | std::string s = R"(Ela escreveu "(sorriso)" no fim)";
| ^
rawerro.cpp:5:56: error: missing terminating " character
5 | std::string s = R"(Ela escreveu "(sorriso)" no fim)";
| ^~
rawerro.cpp: In function ‘int main()’:
rawerro.cpp:5:49: error: expected ‘,’ or ‘;’ before ‘no’
5 | std::string s = R"(Ela escreveu "(sorriso)" no fim)";
| ^~
A raw string terminou no )" depois de sorriso. Daí em diante, o compilador viu no fim) solto (por isso "esperava , ou ; antes de no") e uma aspa no fim que abre um texto que nunca fecha ("faltando o caractere " de fechamento").
A solução é um delimitador: uma palavrinha sua (até 16 caracteres, sem espaços, parênteses ou barras) entre a aspa e o parêntese, na abertura e no fechamento. O texto passa a acabar só em )palavra":
#include <iostream>
#include <string>
int main() {
std::string s = R"x(Ela escreveu "(sorriso)" no fim)x";
std::cout << s << '\n';
}
$ g++ -std=c++23 -Wall -Wextra rawdelim.cpp -o rawdelim $ ./rawdelim Ela escreveu "(sorriso)" no fim
- R"x(
- Abre com o delimitador
x. - )x"
- Fecha. O
)"no meio do texto agora é só texto, porque não tem ox.
Pode ser qualquer palavra: R"json(…)json", R"---(…)---". Escolha uma que não apareça no texto.
7. Literais lado a lado: "a" "b"
Às vezes o texto é só comprido, sem barras nem aspas, e você quer quebrá-lo em várias linhas do código para não precisar rolar a tela. Basta escrever vários literais seguidos, sem nada entre eles: o compilador cola todos num só, antes de compilar.
#include <iostream>
#include <string>
int main() {
std::string aviso = "Atenção: este programa apaga arquivos. "
"Confira a pasta antes de continuar. "
"Digite S para seguir.";
std::cout << aviso << '\n';
std::cout << "linha 1\n"
"linha 2\n"
"linha 3\n";
}
$ g++ -std=c++23 -Wall -Wextra adjacentes.cpp -o adjacentes $ ./adjacentes Atenção: este programa apaga arquivos. Confira a pasta antes de continuar. Digite S para seguir. linha 1 linha 2 linha 3
- "…arquivos. " "Confira…"
- Dois literais vizinhos viram um só:
"…arquivos. Confira…". Não há+, e por isso não há o erroinvalid operandsda aula Juntar Strings: a colagem acontece antes de existir qualquer tipo. - O espaço no fim de cada pedaço
- O compilador cola os pedaços sem pôr nada no meio. Esqueça o espaço e as palavras grudam:
arquivos.Confira. - Sem
\n - Quebrar a linha no código não quebra a linha na tela. Para isso, coloque
\ndentro dos pedaços, como no segundo exemplo.
Qual usar para texto comprido?
| Forma | Quando usar |
|---|---|
| Literais lado a lado | Texto longo de uma linha na tela, ou linhas com \n, mantendo o recuo do código bonito. |
| Raw string em várias linhas | Texto que já é um "bloco" (menu, ajuda, desenho, JSON), sem precisar de \n; o recuo entra no texto. |
+ ou += | Quando algum pedaço é uma variável (aula Juntar Strings). |
Quando dá errado
warning: unknown escape sequence: ‘\p’
O que significa: há uma barra invertida seguida de um caractere que não forma escape nenhum. Quase sempre é um caminho do Windows. O g++ joga a barra fora. E cuidado com o que ele não avisa: \n, \t, \r, \a, \b, \f e \v são válidos, e viram caracteres especiais em silêncio.
Como consertar: \\, raw string R"(…)" ou barras normais /.
error: unable to find string literal operator ‘operator""oi’
#include <iostream>
int main() {
std::cout << "Ela disse "oi"\n";
}
$ g++ -std=c++23 -Wall -Wextra aspa.cpp -o aspa
aspa.cpp: In function ‘int main()’:
aspa.cpp:4:32: error: unable to find string literal operator ‘operator""oi’ with ‘const char [12]’, ‘long unsigned int’ arguments
4 | std::cout << "Ela disse "oi"\n";
| ^~~~
O que significa: a aspa antes de oi fechou o texto "Ela disse ". O oi colado depois da aspa pareceu um sufixo (como o s de "a"s, da aula Juntar Strings), e o compilador procurou um sufixo chamado oi, que não existe. A mensagem é estranha, mas a causa é simples.
Como consertar: escape as aspas internas, "Ela disse \"oi\"\n", ou use R"(Ela disse "oi")".
error: missing terminating " character
#include <iostream>
int main() {
std::cout << "Olá, mundo!\n;
}
$ g++ -std=c++23 -Wall -Wextra aberta.cpp -o aberta
aberta.cpp:4:18: warning: missing terminating " character
4 | std::cout << "Olá, mundo!\n;
| ^
aberta.cpp:4:18: error: missing terminating " character
4 | std::cout << "Olá, mundo!\n;
| ^~~~~~~~~~~~~~~
aberta.cpp: In function ‘int main()’:
aberta.cpp:5:1: error: expected primary-expression before ‘}’ token
5 | }
| ^
O que significa: "falta o caractere " de fechamento". O texto foi aberto e a linha acabou sem fechar. O segundo erro (na linha 5) é consequência: o compilador chegou no } ainda esperando o resto do comando.
Como consertar: feche a aspa: "Olá, mundo!\n";. Se você queria um texto em várias linhas, use uma raw string ou literais lado a lado. Também aparece quando uma raw string termina antes da hora (seção 6).
Referência rápida
| Escrita | Resultado |
|---|---|
"\n" / "\t" | Quebra de linha / Tab |
"\"" / '\'' | Aspa dupla num texto / aspa simples num char |
"\\" | Uma barra invertida |
"\0" | Caractere nulo (fim de texto no estilo C) |
"\r" / "\a" | Volta ao começo da linha / bip |
"\x41" | Byte pelo valor hexadecimal (A) |
"\u00e7" / "\U0001F600" | Caractere Unicode pelo código (ç / 😀) |
R"(…)" | Raw string: sem escapes, pode ter várias linhas C++11 |
R"x(…)x" | Raw string com delimitador, para textos com )" |
"abc" "def" | Literais lado a lado: "abcdef" |
Exercícios
1. Tab
Complete para separar Produto e Preço com um Tab:
2. Barra invertida
Complete o escape para mostrar C:\Temp:
3. Raw string
Complete a abertura da raw string que guarda C:\novos sem escapes:
4. O que std::cout << "C:\novos"; mostra?
5. Quanto vale std::string("a\tb").size()?
6. Qual o resultado de std::string s = "Bom" "dia";?
Missão final: o gato da lojinha
Missão: a Ana quer um gatinho no topo do programa, mas as barras invertidas bagunçaram o desenho (rode e veja o aviso). Reescreva usando uma raw string R"(…)" em várias linhas, sem nenhum aviso, para aparecer:
/\_/\ ( o.o ) > ^ <
#include <iostream>
int main() {
std::cout << " /\_/\\n";
std::cout << "( o.o )\n";
std::cout << " > ^ <\n";
}
#include <iostream>
int main() {
std::cout << R"( /\_/\
( o.o )
> ^ <
)";
}
Para comparar, o programa original, compilado no laboratório:
$ g++ -std=c++23 -Wall -Wextra gato2.cpp -o gato2
gato2.cpp: In function ‘int main()’:
gato2.cpp:4:18: warning: unknown escape sequence: ‘\_’
4 | std::cout << " /\_/\\n";
| ^~~~~~~~~~
$ ./gato2
/_/\n( o.o )
> ^ <
O \_ perdeu a barra, e o \\n do fim virou \\ (uma barra) + n, então a linha não pulou.
Resumo
- A barra invertida começa uma sequência de escape:
\n(linha),\t(Tab),\",\',\\,\0. Cada uma vira um caractere. - Caminhos do Windows entre aspas quebram:
\ne\tviram especiais em silêncio; escapes desconhecidos dãounknown escape sequence. - Acentos podem ir direto no código (UTF-8);
\u00e7e\x41escrevem pelo código. R"(…)"desliga os escapes e aceita várias linhas: ideal para caminhos, regex, aspas e desenhos. Se o texto tiver)", useR"x(…)x".- Literais lado a lado (
"a" "b") são colados pelo compilador: bom para quebrar textos longos no código.