Strings Avançado

A Lição 06 cobriu o básico. Agora vamos nos aprofundar. O que a imutabilidade realmente significa? Que operações de string menos conhecidas mas poderosas existem? Como caracteres chineses são armazenados em computadores? Após esta lição, seu entendimento de strings alcançará um novo nível.


1. Imutabilidade de Strings

Mencionamos brevemente no FAQ da Lição 06 — uma vez criadas, strings não podem ser modificadas. Vamos ver o que isso significa concretamente.

PYTHON
text = "Hello"

# ❌ Não pode modificar assim — isto daria erro
# text[0] = "h"    # TypeError: 'str' object does not support item assignment

Para mudar a primeira letra para minúscula, crie uma nova string:

PYTHON
text = "Hello"
new_text = "h" + text[1:]   # Cria uma nova string
print(new_text)              # hello
print(text)                  # Hello — original inalterado

Benefícios da Imutabilidade

PYTHON
# Referências compartilhadas seguras
a = "Python"
b = a       # b e a apontam para a mesma string

# Não importa o que você faça com a, b não é afetado
a = a.upper()
print(a)    # PYTHON
print(b)    # Python — b ainda mantém o valor original
💡 Dica: A imutabilidade traz segurança de threads e capacidade de hash. Strings podem ser chaves de dicionário (abordaremos depois); listas não podem — porque listas são mutáveis, e alterá-las quebraria a busca do dicionário.

O Custo da Imutabilidade

Cada "modificação" cria um novo objeto. Concatenar muitas strings em um laço é lento:

PYTHON
# ❌ Não recomendado: concatenação de strings em um laço
result = ""
for i in range(10000):
    result += str(i) + ","
# Isso cria 10000 objetos temporários de string

# ✅ Recomendado: colete em uma lista, depois join()
parts = []
for i in range(10000):
    parts.append(str(i))
result = ",".join(parts)
⚠️ Observação: Na codificação diária, concatenar algumas dezenas de strings nunca é uma preocupação de desempenho. Cuidado com concatenação de milhares de strings em um laço. Abaixo de algumas centenas de operações, += é perfeitamente aceitável.

Exemplo: Simulando Métodos de String (Dificuldade ⭐⭐)

PYTHON
# Implementa uma função "capitalize"
def my_capitalize(text):
    if not text:
        return text
    first = text[0].upper()
    rest = text[1:].lower()
    return first + rest

print(my_capitalize("hello WORLD"))      # Hello world
print(my_capitalize("python"))           # Python

# Implementa uma função "replace"
def my_replace(text, old, new):
    parts = text.split(old)     # Divide por old, obtém as partes
    return new.join(parts)      # Rejunta com new

print(my_replace("one, two, one", "one", "1"))    # 1, two, 1
▶ Experimente

2. Métodos de Consulta de String

Além de find() e count() da Lição 06, aqui estão mais métodos úteis.

startswith() e endswith()

PYTHON
# Verifica início/fim da string

url = "https://www.example.com"

print(url.startswith("https"))    # True — protocolo HTTPS
print(url.endswith(".com"))       # True — domínio .com
print(url.endswith(".org"))       # False

# Pode verificar múltiplos de uma vez
print(url.endswith((".com", ".org", ".cn")))   # True — corresponde a .com

Usos Práticos

PYTHON
# Verificação de extensão de arquivo
filename = "relatorio.pdf"

if filename.endswith((".pdf", ".doc", ".docx")):
    print("Arquivo de documento")
elif filename.endswith((".jpg", ".png", ".gif")):
    print("Arquivo de imagem")
elif filename.endswith((".py", ".js", ".html")):
    print("Arquivo de código")
else:
    print("Outro arquivo")

# Verificação de protocolo de URL
link = "ftp://files.example.com"
if link.startswith(("http://", "https://")):
    print("Link web")
elif link.startswith("ftp://"):
    print("Link FTP")

Saída:

TEXT
Arquivo de documento
Link FTP

removeprefix() e removesuffix() (Python 3.9+)

Estes foram adicionados no Python 3.9 para remover limpo prefixos ou sufixos:

PYTHON
url = "https://www.example.com"

# Remove prefixo do protocolo
domain = url.removeprefix("https://")
print(domain)               # www.example.com

# Remove sufixo do domínio
site_name = url.removesuffix(".com")
print(site_name)             # https://www.example

# Se a string não começar/terminar com o texto dado, retorna inalterada
print(url.removeprefix("ftp://"))   # https://www.example.com (inalterado)
💡 Dica: Antes do Python 3.9, remover um prefixo exigia url[len("https://"):] ou url.split("://", 1)[1]. removeprefix() e removesuffix() tornam isso mais intuitivo.


3. Uso Avançado de split()

A Lição 06 cobriu a divisão básica. Vamos explorar variantes mais poderosas.

Limitando o Número de Divisões

PYTHON
# Limite divisões com o parâmetro maxsplit
text = "one,two,three,four,five"

print(text.split(",", 1))       # ['one', 'two,three,four,five']
print(text.split(",", 2))       # ['one', 'two', 'three,four,five']
print(text.split(",", 3))       # ['one', 'two', 'three', 'four,five']

rsplit(): Dividir da Direita para a Esquerda

PYTHON
text = "one,two,three,four,five"

print(text.rsplit(",", 1))      # ['one,two,three,four', 'five']
print(text.rsplit(",", 2))      # ['one,two,three', 'four', 'five']

splitlines(): Dividir por Linhas

PYTHON
multiline = """Primeira linha
Segunda linha
Terceira linha"""

lines = multiline.splitlines()
print(lines)                    # ['Primeira linha', 'Segunda linha', 'Terceira linha']

# Manter quebras de linha
lines = multiline.splitlines(True)
print(lines)                    # ['Primeira linha\n', 'Segunda linha\n', 'Terceira linha']

partition() e rpartition()

partition() não apenas divide mas também retém a posição do separador:

PYTHON
text = "usuario@example.com"

# Divide por @ em três partes: (esquerda, separador, direita)
parts = text.partition("@")
print(parts)                    # ('usuario', '@', 'example.com')

# Se separador não encontrado, retorna (string_original, '', '')
print("hello".partition("@"))   # ('hello', '', '')

# rpartition pesquisa da direita
path = "/home/usuario/documentos/arquivo.txt"
last_slash = path.rpartition("/")
print(last_slash)               # ('/home/usuario/documentos', '/', 'arquivo.txt')

partition() é melhor que split() para cenários de "extração" — informa exatamente o que cada uma das três partes é.

Exemplo: Análise de URL (Dificuldade ⭐⭐)

PYTHON
# Analisa uma URL usando partition
url = "https://www.example.com:8080/path/page.html?name=test&page=1"

# Extrai protocolo
proto, _, rest = url.partition("://")
print(f"Protocolo: {proto}")        # https

# Extrai host e caminho
host_part, _, path_and_query = rest.partition("/")
print(f"Host: {host_part}")    # www.example.com:8080

# Extrai porta
host, _, port = host_part.partition(":")
print(f"Domínio: {host}")         # www.example.com
print(f"Porta: {port}")         # 8080

# Extrai caminho e consulta
path, _, query = path_and_query.partition("?")
print(f"Caminho: /{path}")        # /path/page.html
print(f"Consulta: {query}")        # name=test&page=1
▶ Experimente

Saída:

TEXT
Protocolo: https
Host: www.example.com:8080
Domínio: www.example.com
Porta: 8080
Caminho: /path/page.html
Consulta: name=test&page=1

4. Alinhamento e Preenchimento de String

Python fornece métodos para alinhar strings dentro de uma largura especificada — esquerda, direita ou centro.

PYTHON
text = "Python"

print(text.ljust(10))           # 'Python    ' (alinhado à esquerda, preenchido com espaços à direita)
print(text.rjust(10))           # '    Python' (alinhado à direita, preenchido à esquerda)
print(text.center(10))          # '  Python  ' (centralizado, preenchido em ambos os lados)

# Pode especificar um caractere de preenchimento
print(text.ljust(10, "-"))      # 'Python----'
print(text.rjust(10, "-"))      # '----Python'
print(text.center(10, "-"))     # '--Python--'

zfill(): Preenchimento com Zeros

Comumente usado para IDs numéricos:

PYTHON
print("42".zfill(5))            # 00042
print("-42".zfill(5))           # -0042 — sinal negativo vem primeiro
print("3.14".zfill(8))          # 00003.14

# Gera IDs de 3 dígitos
for i in range(1, 11):
    filename = f"foto_{str(i).zfill(3)}.jpg"
    print(filename, end="  ")

Saída:

TEXT
foto_001.jpg  foto_002.jpg  foto_003.jpg  foto_004.jpg  foto_005.jpg
foto_006.jpg  foto_007.jpg  foto_008.jpg  foto_009.jpg  foto_010.jpg

Exemplo: Gerando um Relatório Alinhado (Dificuldade ⭐⭐)

PYTHON
# Alinha uma tabela usando rjust e ljust
items = [
    ("Maçã", 5.0, 3),
    ("Banana", 3.5, 5),
    ("Leite", 12.0, 2),
]

# Cabeçalhos
header_name = "Item".ljust(8)
header_price = "Preço".rjust(6)
header_qty = "Qtd".rjust(4)
header_total = "Total".rjust(6)
print(f"{header_name}{header_price}{header_qty}{header_total}")
print("-" * 28)

# Linhas de dados
for name, price, qty in items:
    total = price * qty
    name_col = name.ljust(8)
    price_col = f"{price:.1f}".rjust(6)
    qty_col = str(qty).rjust(4)
    total_col = f"{total:.1f}".rjust(6)
    print(f"{name_col}{price_col}{qty_col}{total_col}")
▶ Experimente

Saída:

TEXT
Item     Preço  Qtd Total
────────────────────────────
Maçã      5.0    3  15.0
Banana     3.5    5  17.5
Leite     12.0    2  24.0

5. Codificação de Caracteres: ord() e chr()

Cada caractere em um computador corresponde a um número (ponto de código). ord() obtém a codificação numérica de um caractere; chr() faz o inverso.

PYTHON
# Visualiza pontos de código Unicode
print(ord("A"))          # 65
print(ord("ç"))         # 231 (cedilha)
print(ord("❤"))          # 10084

# Obtém caractere a partir do ponto de código
print(chr(65))           # A
print(chr(231))         # ç
print(chr(10084))        # ❤

# Gera todas as letras maiúsculas
for code in range(ord("A"), ord("Z") + 1):
    print(chr(code), end=" ")
# Saída: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

O que é Unicode

Simplificando, Unicode é um padrão global de codificação de caracteres — atribui um número único a cada caractere de todos os sistemas de escrita do mundo (chinês, inglês, árabe, japonês, etc.). Python 3 armazena todas as strings internamente como Unicode, então suporta nativamente múltiplos idiomas.

PYTHON
# Strings Python 3 suportam nativamente Unicode
text = "Hello, 你好, مرحبا, こんにちは"

print(text)                     # Todos exibidos corretamente
print(len(text))                # Contagem de caracteres

# Você também pode representar caracteres com escapes \u
print("\u0048")                 # H
print("\u00e7")                 # ç
⚠️ Observação: Em Python 2, as strings eram divididas em str (bytes) e unicode (texto), causando erros frequentes de codificação. Python 3 as unificou — todas as strings são Unicode, tratando chinês e inglês igualmente. Esta é uma das melhores decisões de design do Python 3.

Codificação e Decodificação

Embora Python use Unicode internamente, o armazenamento de arquivos e a transmissão de rede usam bytes. Então você precisa de codificação (encode) e decodificação (decode):

PYTHON
text = "Hello, 世界"

# Codificação: string → bytes
utf8_bytes = text.encode("utf-8")
print(utf8_bytes)               # b'\xe4\xbd\xa0\xe5\xa5\xbd...'
print(len(utf8_bytes))          # Número de bytes em UTF-8

# Decodificação: bytes → string
decoded = utf8_bytes.decode("utf-8")
print(decoded)                  # Hello, 世界

# Se encode/decode não coincidirem, ocorre erro
# gbk_bytes.decode("utf-8")    # UnicodeDecodeError!
💡 Dica: 99% do desenvolvimento diário funciona bem com UTF-8. UTF-8 é a codificação padrão da internet, compatível com ASCII e capaz de representar todos os caracteres Unicode. Se você encontrar texto ilegível ao ler arquivos, o problema é quase certamente a codificação — especifique encoding="utf-8" ao abrir o arquivo.


6. Suplemento de Formatação de String

Além das f-strings, Python tem dois métodos de formatação mais antigos. Embora as f-strings sejam o padrão moderno, você pode encontrar estes em código mais antigo.

Método format() (Python 3.0+)

PYTHON
# Por posição
print("Meu nome é {}, tenho {} anos.".format("Alice", 18))
# Meu nome é Alice, tenho 18 anos.

# Por nome
print("Meu nome é {nome}, tenho {idade} anos.".format(nome="Bob", idade=22))
# Meu nome é Bob, tenho 22 anos.

# Formatação de números
print("Pi: {:.3f}".format(3.14159))
# Pi: 3.142

Formatação % (Era Python 2)

PYTHON
# Você pode ver isso em código mais antigo
print("Meu nome é %s, tenho %d anos." % ("Alice", 25))
# Meu nome é Alice, tenho 25 anos.

print("Pi: %.2f" % 3.14159)
# Pi: 3.14
💡 Dica: f-strings (Python 3.6+) são a melhor escolha — mais rápidas, mais legíveis, mais concisas. Para código novo, sempre use f-strings. format() ainda é útil para "formatação atrasada" (definir um template agora, preencher valores depois). A formatação % é apenas para reconhecimento quando você a encontrar; não a use em código novo.


Casos de Uso Comuns


❓ Perguntas Frequentes

P: Por que as strings do Python 3 são Unicode mas as do Python 2 não eram? R: A era do Python 2 foi atormentada por uma confusão de codificações (ASCII, Latin-1, GBK, Shift-JIS coexistindo). O tratamento de strings era dividido entre "bytes" e "texto" — dois sistemas separados, muito propensos a erros. Python 3 fez uma ruptura limpa: todas as strings são Unicode (texto); dados de bytes são tratados separadamente pelo tipo bytes. Isso torna as operações de string intuitivas — len("ç") é sempre 1 em qualquer sistema. A desvantagem é que você precisa de encode/decode manual para E/S de arquivo ou rede. ⚠️ P: Quando devo usar split() vs partition()? R: Para divisão simples, use split(). Quando você precisa do próprio separador, use partition(). partition() sempre retorna três elementos (esquerda, separador, direita), útil para desempacotamento de tupla: esq, sep, dir = texto.partition(":"). split() retorna uma lista de comprimento variável. Se o delimitador aparece apenas uma vez e você precisa do conteúdo em ambos os lados, partition() é a melhor escolha. P: Qual a diferença entre ljust() e a f-string {valor:10}? R: Eles produzem o mesmo resultado — text.ljust(10) equivale a f"{text:<10}". A versão f-string é mais flexível (combina alinhamento com formatação de números), enquanto a chamada de método é mais intuitiva. É uma questão de preferência — escolha uma e mantenha consistência. Símbolos de alinhamento em f-strings: < esquerda, > direita, ^ centro: f"{text:^10}". P: Qual a diferença entre UTF-8 e GBK? Por que meu arquivo Python às vezes salva com caracteres ilegíveis? R: UTF-8 é o padrão internacional — 1-4 bytes por caractere, universal. GBK é um padrão chinês (chinês simplificado) — 1-2 bytes por caractere, suporta apenas chinês e inglês. Se seu arquivo Python for salvo como GBK mas contiver texto em japonês ou árabe, ocorrerá erro ou exibição ilegível. Solução: salve todos os arquivos Python como UTF-8 — Python 3 usa UTF-8 como padrão.


📖 Resumo


📝 Atividades

  1. Básico (Dificuldade ⭐): Dado files = ["relatorio.pdf", "foto.jpg", "script.py", "notas.txt", "index.html"], use endswith() para filtrar:

    • Todos os arquivos de imagem (.jpg, .png, .gif)
    • Todos os arquivos de documento (.pdf, .doc, .docx, .txt)
    • Todos os arquivos de código (.py, .js, .html, .css)
  2. Intermediário (Dificuldade ⭐⭐): Escreva um "renomeador de arquivos em lote." Dado photos = ["IMG_1.jpg", "IMG_2.jpg", ..., "IMG_12.jpg"], renomeie-os para photo_001.jpg, photo_002.jpg, ..., photo_012.jpg. Dica: Use partition() para dividir nome e extensão; use zfill() para preenchimento com zeros.

  3. Desafio (Dificuldade ⭐⭐⭐): Escreva um "mecanismo de template simples." Dado uma string de template template e um dicionário data, substitua espaços reservados {variavel} por valores reais.

    PYTHON
    template = "Prezado {nome}, seu pedido {pedido_id} foi {status}. Previsão de entrega: {prazo_entrega}."
    
    data = {
        "nome": "Alice",
        "pedido_id": "20260623001",
        "status": "enviado",
        "prazo_entrega": "3 dias úteis"
    }
    
    # Seu código: implemente replace_template(template, data)
    # Saída: Prezado Alice, seu pedido 20260623001 foi enviado. Previsão de entrega: 3 dias úteis.
    

    Requisito extra: Se uma variável do template não existir em data, deixe {variavel} como está (não substitua). Dica: Itere sobre pares chave-valor em data, chamando replace() no template para cada um.

100%