Codificação de Caracteres macOS vs Windows: Como Prevenir Erros de Sincronização

Resumo rápido: neste guia encontra a explicação técnica do problema NFD vs NFC, os cenários de falha mais comuns em OneDrive, SharePoint e NAS, as boas práticas preventivas e um script Python completo para normalizar nomes de ficheiros e pastas no macOS.

Quando um utilizador de macOS guarda um ficheiro chamado Configuração.docx e outro utilizador Windows abre a mesma pasta partilhada, pode ver dois ficheiros em vez de um — ou não ver nenhum. Não é um bug do serviço de sincronização: é uma diferença fundamental na forma como os dois sistemas operativos representam caracteres com acentos a nível de bytes.

Este artigo explica a causa técnica (Unicode NFD vs NFC), os problemas que provoca em OneDrive, SharePoint, Dropbox, Git e NAS com SMB, e como resolver — incluindo um script Python pronto a usar para normalizar pastas inteiras no macOS sem perda de dados.

⚠ Ficheiros duplicados após sync não são erro do OneDrive

Se vê ficheiros como relatorio.docx e relatório.docx em simultâneo após sincronizar entre Mac e Windows, a causa é quase sempre codificação Unicode — o mesmo nome, representado por bytes diferentes, é tratado como dois ficheiros distintos pelo serviço de sync.

1. NFD vs NFC: a causa técnica do problema

O Unicode define múltiplas formas de representar o mesmo caracter visível. Um ã pode ser guardado como um único code point (U+00E3 — forma composta NFC) ou como dois code points separados: a letra a seguida de um tilde combinante (U+0061 + U+0303 — forma decomposta NFD). O resultado visual é idêntico; os bytes em disco são diferentes.

Sistema operativo Normalização Representação de “ã” Sistema de ficheiros
macOS (APFS / HFS+) NFD — decomposto a + tilde combinante (2 bytes) APFS, HFS+
Windows NFC — composto ã (1 byte — U+00E3) NTFS, FAT32, exFAT
Linux / ext4 Nenhuma (passthrough) Depende da aplicação que escreveu ext4, XFS, Btrfs

O macOS aplica NFD automaticamente ao nível do sistema de ficheiros — qualquer ficheiro criado com um nome acentuado fica guardado em NFD, independentemente da aplicação usada. O Windows usa NFC. Quando um serviço de sincronização compara os dois, calcula hashes diferentes e trata-os como ficheiros distintos.

2. Problemas concretos em sincronização

Sintoma Causa Serviços afectados
Ficheiros duplicados após sync NFD e NFC tratados como ficheiros distintos OneDrive, Dropbox, SharePoint
Conflitos fantasma (sem alterações) Hash difere apesar do conteúdo ser igual OneDrive, Google Drive, Dropbox
Ficheiro desaparece no Windows Nome contém caracteres proibidos no NTFS OneDrive, SharePoint, NAS SMB
Erro de path too long no Windows Caminho ultrapassa 260 caracteres (limite NTFS por omissão) SharePoint (limite 400 chars), OneDrive
git status mostra ficheiros alterados sem edições NFD do Mac vs NFC no repositório remoto Git, GitHub, Azure DevOps

3. Caracteres proibidos no Windows NTFS

Além da questão NFD/NFC, o NTFS proíbe um conjunto de caracteres em nomes de ficheiros que o macOS permite livremente. Qualquer ficheiro macOS com estes caracteres no nome não sincroniza para o lado Windows — e frequentemente desaparece sem aviso:

# Caracteres PROIBIDOS no Windows NTFS (válidos no macOS)
\  /  :  *  ?  "  <  >  |

# Nomes reservados pelo Windows (mesmo sem extensão)
CON  PRN  AUX  NUL  COM1-COM9  LPT1-LPT9

# Outros problemas comuns
. nome          # ponto inicial = oculto no macOS/Linux, visível no Windows
nome.           # ponto final = inválido no Windows
nome com espaço # espaços no início ou fim do nome

# Caracteres adicionais proibidos no SharePoint Online
~  #  %  &  *  {  }  '  "

4. Boas práticas preventivas por serviço

A regra de ouro em ambientes mistos é adoptar NFC + sem caracteres especiais NTFS como convenção de equipa para nomes de ficheiros e pastas. Abaixo estão as configurações específicas por serviço.

Exemplos de nomes seguros vs. problemáticos:

# SEGUROS: sem acentos, sem caracteres especiais
relatorio-financeiro-2024.xlsx
configuracao-vpn.docx
reuniao-equipa-marco-2024.pptx

# PROBLEMÁTICOS: acentos + caracteres especiais NTFS
relatório financeiro: Q1/2024.xlsx   # acentos + dois pontos + barra
configuração VPN?.docx               # acento + ponto de interrogação
Reunião equipa — março 2024.pptx     # acento + em dash
Serviço Configuração recomendada Limite a ter em atenção
OneDrive Sem configuração especial — evitar caracteres proibidos e caminhos longos Caminho total < 400 caracteres
SharePoint Online Evitar também: ~ # % & * { } ' " Caminho total < 400 chars; nome de ficheiro < 128 chars
Dropbox Cliente Windows normaliza para NFC automaticamente, mas pode criar duplicados se o Mac sincronizar NFD primeiro Usar Selective Sync durante migração
NAS Synology (SMB) DSM → Serviços de Ficheiros → SMB → activar Support unicode NFD encoding Resolve a maioria dos problemas com clientes macOS
Windows (Long Path) Activar suporte a caminhos longos via Group Policy ou registo Disponível desde Windows 10 1607
# Activar suporte a caminhos longos no Windows (PowerShell como admin)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1

# Via Group Policy (para domínios AD):
# Computer Configuration → Administrative Templates → System → Filesystem
# → Enable Win32 long paths → Enabled

5. Script Python: normalizar pastas no macOS

O script fix_accents.py percorre uma pasta e todas as suas subpastas, remove acentos e diacríticos dos nomes de ficheiros e pastas, e elimina caracteres proibidos no NTFS. Funciona com o Python 3 incluído no macOS — sem dependências externas.

🛑 O erro já aconteceu? Faz backup antes de qualquer acção

Se tens ficheiros duplicados, nomes corrompidos ou itens em falta depois de uma sincronização, não tentes corrigir imediatamente. A renomeação em massa é irreversível — um conflito não detectado pode sobrepor dados. O procedimento seguro é:

  1. Pausar a sincronização do OneDrive / Dropbox / Google Drive antes de tocar em qualquer ficheiro
  2. Fazer backup completo da pasta afectada para um destino externo (Time Machine, disco externo ou cópia manual para outra localização)
  3. Verificar o backup antes de continuar — confirmar que todos os ficheiros estão acessíveis na cópia
  4. Só depois executar o script com --dry-run para avaliar as alterações
  5. Aplicar as alterações e reactivar a sincronização apenas após validar o resultado

📌 Opção rápida de backup no macOS antes de executar o script:
cp -R ~/Documents/Projecto ~/Desktop/Projecto_backup_$(date +%Y%m%d)

💡 Executar sempre –dry-run primeiro

O modo de simulação mostra todas as alterações que seriam feitas sem renomear nada. É a forma segura de validar o resultado antes de aplicar. O script pede confirmação explícita antes de qualquer alteração real e detecta conflitos de nomes automaticamente.

Utilização:

# Passo 1: Simular sem alterar nada (sempre primeiro)
python3 fix_accents.py ~/Documents/Projecto --dry-run

# Passo 2: Aplicar as alterações (pede confirmação)
python3 fix_accents.py ~/Documents/Projecto

# Com log de todas as alterações para ficheiro
python3 fix_accents.py ~/Documents/Projecto --log alteracoes.txt

# Manter maiúsculas/minúsculas originais (por omissão converte para minúsculas)
python3 fix_accents.py ~/Documents/Projecto --no-lowercase

Exemplos de transformações aplicadas pelo script:

Nome original (macOS NFD) Resultado (NFC seguro) O que foi corrigido
Configuração/ Configuracao/ Diacrítico ã → a, ç → c
relatório final.docx relatorio-final.docx ó → o, espaço → hífen
Q1: Resultados?.xlsx Q1-Resultados.xlsx : e ? removidos (NTFS)
café — março.txt cafe-marco.txt é → e, em-dash → hífen, ç → c
.DS_Store, ._ficheiro Ignorado Ficheiros de sistema macOS nunca são tocados
nome (já existe) Conflito detectado Avisa e salta — nunca sobrescreve

Código do script (fix_accents.py):

#!/usr/bin/env python3
# fix_accents.py — Remove acentos e caracteres especiais de nomes de
# ficheiros e pastas para compatibilidade macOS/Windows (NFD → ASCII seguro)
# Não requer dependências externas — funciona com Python 3 nativo do macOS

import os, sys, re, unicodedata, argparse, logging
from pathlib import Path
from datetime import datetime

# Mapeamento de caracteres especiais sem equivalente ASCII directo
EXTRA_MAP = {
    '–': '-',   # en dash
    '—': '-',   # em dash
    'ª': 'a',   # ordinal feminino ª
    'º': 'o',   # ordinal masculino º
    'ß': 'ss',  # ß
    'æ': 'ae',  # æ
    'ø': 'o',   # ø
    '…': '.',   # reticências …
}
NTFS_FORBIDDEN = r'[\/:*?"<>|]'

def slugify_name(name, lowercase=False):
    if name.startswith('.'): return name  # preservar ficheiros ocultos
    stem, *ext = name.rsplit('.', 1) if '.' in name else (name,)
    ext = ('.' + ext[0]) if ext else ''
    r = unicodedata.normalize('NFC', stem)
    for src, dst in EXTRA_MAP.items(): r = r.replace(src, dst)
    r = unicodedata.normalize('NFD', r)
    r = ''.join(c for c in r if unicodedata.category(c) != 'Mn')
    r = re.sub(NTFS_FORBIDDEN, '', r)
    r = re.sub(r'[\s]+', '-', r)
    r = re.sub(r'[^\w\-.]', '', r)
    r = re.sub(r'-{2,}', '-', r).strip('-').strip('.') or 'ficheiro'
    if lowercase: r, ext = r.lower(), ext.lower()
    return r + ext

def process_directory(root, dry_run, lowercase, logger):
    renamed = errors = unchanged = 0
    for dirpath, dirnames, filenames in os.walk(root, topdown=False):
        cur = Path(dirpath)
        for name in filenames + dirnames:
            if name in ('.DS_Store', '.localized') or name.startswith('._'):
                continue
            new = slugify_name(name, lowercase)
            if new == name: unchanged += 1; continue
            old_p, new_p = cur / name, cur / new
            if new_p.exists() and new_p != old_p:
                print(f'  CONFLITO  {name} → {new} (ignorado)'); errors += 1; continue
            if dry_run:
                print(f'  DRY-RUN  {name}  →  {new}'); renamed += 1
            else:
                try: old_p.rename(new_p); print(f'  OK  {name}  →  {new}'); renamed += 1
                except Exception as e: print(f'  ERRO  {name}: {e}'); errors += 1
    return renamed, errors, unchanged

def main():
    p = argparse.ArgumentParser()
    p.add_argument('path'); p.add_argument('--dry-run', action='store_true')
    p.add_argument('--no-lowercase', action='store_true'); p.add_argument('--log')
    args = p.parse_args()
    root = Path(args.path).expanduser().resolve()
    if not root.is_dir(): print(f'Erro: {root} não é uma pasta'); sys.exit(1)
    logger = logging.getLogger('fix_accents')
    if args.log:
        fh = logging.FileHandler(args.log, encoding='utf-8')
        fh.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
        logger.addHandler(fh); logger.setLevel(logging.DEBUG)
    print(f'
Pasta: {root}')
    print(f'Modo: {"DRY-RUN" if args.dry_run else "REAL"}
')
    if not args.dry_run:
        r = input('ATENÇÃO: operação irreversível. Tens backup? (s/N): ').strip().lower()
        if r not in ('s','sim','y','yes'): print('Cancelado.'); sys.exit(0)
    renamed, errors, unchanged = process_directory(root, args.dry_run, not args.no_lowercase, logger)
    print(f'
{"Seriam renomeados" if args.dry_run else "Renomeados"}: {renamed}')
    print(f'Sem alterações: {unchanged} | Erros/Conflitos: {errors}
')

if __name__ == '__main__': main()

6. Configuração Git para ambientes mistos

O Git tem um comportamento específico em relação a NFD/NFC que afecta repositórios partilhados entre utilizadores de macOS e Windows. Sem a configuração correcta, o git status mostra ficheiros como modificados em Macs, mesmo sem qualquer edição de conteúdo.

# Aplicar globalmente em todos os repositórios Git do Mac
git config --global core.precomposeunicode true   # força NFC nos nomes de ficheiros
git config --global core.quotepath false            # mostra acentos no terminal sem escape

# Verificar a configuração actual
git config --global --list | grep -E "unicode|quote"

# Para um repositório específico já existente
cd /caminho/para/repositorio
git config core.precomposeunicode true

# Detectar ficheiros NFD no repositório (mostra nomes com escape Ã\...)
git ls-files | cat -v | grep '\\'

# Verificar se o .gitattributes está a normalizar correctamente
# Adicionar ao .gitattributes do projecto:
* text=auto eol=lf

7. Problemas comuns e resolução

Problema Causa Resolução
Ficheiros duplicados após sincronizar NFD (Mac) vs NFC (Windows) tratados como ficheiros distintos Executar fix_accents.py no Mac antes de sincronizar; adoptar política de nomes sem acentos
Ficheiro criado no Mac não aparece no Windows Nome contém : * ? < > | (proibidos no NTFS) Renomear o ficheiro no Mac removendo os caracteres problemáticos; usar o script com --dry-run para identificar
git status mostra alterações sem edições NFD do Mac vs NFC no repositório remoto git config --global core.precomposeunicode true
Erro 0x8007007B no OneDrive (nome inválido) Caractere proibido no NTFS no nome do ficheiro ou pasta Identificar com o script em modo --dry-run e renomear; o OneDrive bloqueia o ficheiro até ser corrigido
NAS Synology mostra nomes corrompidos no Mac SMB não está configurado para suporte NFD DSM → Serviços de Ficheiros → SMB → activar Support unicode NFD encoding
Script Python diz “Conflito detectado” Já existe um ficheiro com o nome normalizado na mesma pasta Resolver manualmente: verificar qual o ficheiro a manter, eliminar o duplicado e voltar a executar o script

✓ Checklist para ambientes mistos macOS / Windows

  • Se o erro já ocorreu: pausar a sync, fazer backup completo da pasta afectada antes de qualquer acção — cp -R ~/pasta ~/Desktop/pasta_backup_$(date +%Y%m%d)
  • Política de equipa: nomes de ficheiros sem acentos e sem caracteres especiais NTFS
  • git config --global core.precomposeunicode true em todos os Macs
  • NAS Synology: SMB com Support unicode NFD encoding activado
  • SharePoint: verificar limite de 400 caracteres no caminho total
  • Windows: activar Long Path Support (LongPathsEnabled = 1)
  • Executar fix_accents.py --dry-run antes de qualquer migração de ficheiros

FAQ

Porquê é que o macOS usa NFD em vez de NFC?

O sistema de ficheiros HFS+ da Apple foi desenhado para usar NFD como normalização canónica. O APFS manteve este comportamento por compatibilidade. É uma decisão de design histórica que não implica que um sistema seja mais correcto que o outro — ambos os formatos são Unicode válido.

O script altera o conteúdo dos ficheiros?

Não. O script fix_accents.py altera exclusivamente os nomes de ficheiros e pastas — nunca o conteúdo interno dos ficheiros. Um .docx, .xlsx ou .pdf com texto acentuado fica intacto.

Posso usar o script em pastas já sincronizadas com o OneDrive activo?

Sim, mas recomenda-se pausar a sincronização do OneDrive antes de executar o script e reactivá-la depois. Renomear ficheiros com o cliente de sincronização activo pode gerar conflitos desnecessários durante o processo.

Este artigo foi útil?

Duarte Spínola

Deixe um Comentário