Why Your Filenames Turn Into Question Marks and Strange Symbols

A folder of files with names in Japanese, Cyrillic, or simply French accents arrives on another system as a wall of question marks or nonsense symbols. The files themselves usually open perfectly. This page explains what each symptom means, which kinds of damage are reversible, and how to repair a whole folder at once.

Read the symptom first

Question marks mean the characters were destroyed in a conversion — unrecoverable from the file itself. Sequences like é or æ–‡ mean the bytes are intact and being decoded wrongly — fully recoverable. Establish which you have before doing anything else.

Why this happens at all

Every operating system stores filenames differently, and they disagree in ways that matter.

SystemStores filenames asEncoding declared?
Windows (NTFS)UTF-16 code unitsYes — always Unicode
macOS (APFS/HFS+)UTF-8, normalised to NFDYes
Linux (ext4, etc.)Raw bytesNo — any bytes except / and null
FAT32 / exFATCode page, plus UTF-16 long namesPartially
ZIP archivesBytes, with an optional UTF-8 flagOften not

Linux is the interesting case. A filename there is simply a byte sequence — the kernel does not know or care what encoding it is in. Your terminal decodes those bytes according to your locale. So the same file displays correctly under LANG=en_GB.UTF-8 and as garbage under a Latin-1 locale, without anything about the file having changed.

This flexibility is why Linux never rejects a filename and also why it can silently accumulate files whose names are in three different encodings in the same directory.

Reading the symptoms

You seeMeaningRecoverable?
?????.txtConverted to a charset that could not represent itNo
été.txtUTF-8 bytes decoded as Latin-1Yes
文書.txtUTF-8 decoded as Latin-1 (CJK)Yes
привет.txtUTF-8 decoded as Latin-1 (Cyrillic)Yes
□□□.txtCorrect bytes, font lacks the glyphsNothing to fix
file_.txtIllegal characters replacedNo
FILE~1.TXTTruncated to an 8.3 short nameSometimes

💡 Recognising mojibake by sight

UTF-8 encodes accented Latin characters as two bytes starting with 0xC3, which is à in Latin-1. So a capital à before another symbol is the classic signature of UTF-8 read as Latin-1.

CJK characters are three bytes beginning 0xE40xE9, which appear as ä through é followed by two more symbols. Cyrillic starts with 0xD0 or 0xD1, showing as Ð or Ñ. Once you can spot the leading byte, you can identify the original script from the mojibake alone.

Where names get broken

ZIP archives

The original ZIP format had no field for declaring filename encoding. Tools wrote names in the local code page and readers guessed. A later revision added a flag bit meaning "these names are UTF-8", and modern tools set it — but archives from older software, and tools that ignore the flag, still produce mangled names.

# Tell unzip which encoding was actually used unzip -O CP932 japanese.zip # Shift-JIS unzip -O CP936 chinese.zip # GBK unzip -O CP949 korean.zip # EUC-KR unzip -O CP1251 russian.zip # Cyrillic # List without extracting, to check before committing unzip -l -O CP932 japanese.zip

FTP and SFTP

Classic FTP predates Unicode and transmits filenames as bytes with no encoding negotiation. The UTF8 feature exists but is not universally implemented, and many clients simply use the local code page. SFTP is better — it specifies UTF-8 — but not every server implementation honours it.

Network shares

SMB handles Unicode correctly in modern versions. Problems arise with older NAS devices, Samba servers with a misconfigured unix charset, or shares mounted with an explicit charset option that does not match reality:

# Mount with the correct character set mount -t cifs //server/share /mnt \ -o iocharset=utf8,username=user # Check what Samba is configured for testparm -s | grep -i charset

Email attachments

Attachment filenames must be encoded per RFC 2231 to carry non-ASCII characters. Many clients still use the older RFC 2047 word encoding, or send raw bytes. The result is a downloaded file with a mangled name even though the attachment itself is intact.

Web downloads

# Correct: both forms, for old and new clients Content-Disposition: attachment; filename="report.pdf"; filename*=UTF-8''rapport-d%C3%A9taill%C3%A9.pdf

The filename* parameter carries a percent-encoded UTF-8 name; plain filename provides an ASCII fallback. Sending only the plain form with non-ASCII bytes produces mangled downloads in most browsers.

Repairing mojibake in bulk

When the bytes are intact and merely misread, convmv fixes an entire tree. It defaults to a dry run, which is the correct default:

# Preview — shows what would change, changes nothing convmv -f CP1251 -t UTF-8 -r /path/to/files # Apply once the preview looks right convmv -f CP1251 -t UTF-8 -r --notest /path/to/files # The classic double-encoding case: # UTF-8 that was read as Latin-1 and re-encoded as UTF-8 convmv -f UTF-8 -t Latin1 -r --notest /path/to/files # macOS NFD → NFC normalisation convmv -f UTF-8 -t UTF-8 --nfc -r --notest /path/to/files

Note that third command, which looks wrong and is correct. When UTF-8 bytes are misread as Latin-1 and then saved as UTF-8, each original byte becomes two. Converting from UTF-8 to Latin-1 reverses exactly that step and recovers the original bytes.

# Manual repair in Python, for a single name broken = "été.txt" fixed = broken.encode('latin-1').decode('utf-8') print(fixed) # été.txt # Same idea in PowerShell $bytes = [System.Text.Encoding]::GetEncoding(28591).GetBytes($broken) [System.Text.Encoding]::UTF8.GetString($bytes)

⚠️ Question marks are permanent

If the name contains literal ? characters, the original bytes no longer exist. Converting a name to a charset that cannot represent a character replaces it, and the replacement carries no record of what was there.

The only recovery routes are external: the original source, a backup, an archive that still holds the correct name, or metadata inside the file. A scanned document might carry the original title in its properties; a photo might identify itself by date. The filename itself is gone.

macOS and the NFD problem

Unicode allows é to be written two ways: as the single code point U+00E9, or as e followed by a combining acute accent U+0301. They look identical and are different byte sequences.

macOS stores filenames decomposed (NFD). Windows and Linux normally store what you typed, which is usually composed (NFC). So a file named café.pdf on a Mac has different bytes from the same name created on Linux.

# Same visible name, different bytes macOS: c a f e ́ # 6 bytes — e + combining accent Linux: c a f é # 5 bytes — precomposed é # Which means this can genuinely fail [ "café.pdf" = "café.pdf" ] && echo same || echo different

The practical consequences are subtle and irritating: a script that matches filenames finds nothing, a git repository shows a file as both deleted and added, an rsync between a Mac and a Linux server copies files that already exist. Normalise both sides to NFC before comparing.

Characters that are simply not allowed

CharacterWindowsmacOSLinux
/NoNoNo
\ : * ? " < > |NoYesYes
:NoShown as / in FinderYes
Trailing space or dotNoYesYes
CON PRN AUX NUL COM1 LPT1ReservedYesYes
Case-only differencesCollideCollide by defaultDistinct

The case row causes real data loss. Linux treats README and Readme as two files. Copy that folder to Windows or a default macOS volume and they collide — one silently overwrites the other. Extracting an archive containing both is the usual way people discover this.

Preventing it

✅ Rules for filenames that travel

  • ASCII letters, digits, hyphen, underscore and dot only for anything crossing systems.
  • No spaces — they need quoting in every shell and are encoded in URLs.
  • Hyphens rather than underscores for anything web-facing; search engines treat hyphens as word separators.
  • Lowercase throughout — sidesteps every case-sensitivity difference.
  • ISO dates as prefixes2026-08-02-report.pdf sorts chronologically as plain text.
  • Keep the full path under 200 characters for Windows compatibility.
  • Put the human-readable title in metadata, not in the filename, when it needs non-ASCII characters.
# Bulk-rename to safe names, preserving the original # in an extended attribute so nothing is lost for f in *; do safe=$(echo "$f" | iconv -f UTF-8 -t ASCII//TRANSLIT \ | tr '[:upper:]' '[:lower:]' \ | tr -cs '[:alnum:].' '-') [ "$f" != "$safe" ] && mv -n "$f" "$safe" done # //TRANSLIT transliterates rather than dropping: # é → e, ü → u, ß → ss, 文 → ?

Diagnostic steps

  1. Look at the actual bytes. ls | hexdump -C shows what is really stored rather than what your terminal renders.
  2. Check your locale. locale — if it is not a UTF-8 locale, the problem may be entirely local to your session.
  3. Identify the symptom. Question marks are permanent; mojibake is recoverable.
  4. Work out the original encoding from the leading bytes of the mojibake.
  5. Dry-run convmv and read the output before applying it.
  6. Normalise to NFC if macOS is anywhere in the chain.
  7. Fix the source so it does not recur — the transfer method is usually where it broke.

Encoding text for a URL?

Percent-encode and decode URLs correctly, including non-ASCII characters — instantly, in your browser.

Open the URL Encoder →

Summary

  • Question marks mean the data is gone. Mojibake means it is intact and misread.
  • A leading à is the signature of UTF-8 read as Latin-1.
  • Linux filenames are raw bytes with no declared encoding — display depends on your locale.
  • ZIP archives often omit the encoding. unzip -O lets you specify it.
  • macOS decomposes to NFD, which breaks comparison against NFC systems.
  • convmv repairs whole trees and dry-runs by default.
  • Use ASCII, lowercase, hyphens and ISO dates for anything that crosses systems.
  • Case-only differences collide when moving from Linux to Windows or macOS.

Frequently Asked Questions

Why did my filenames turn into question marks?

The name was converted to a character set that could not represent those characters, and every unrepresentable character was replaced with a literal question mark. Unlike mojibake, this is not a display problem — the original characters are genuinely gone from the filename and cannot be recovered from the file itself.

What is the difference between question marks and characters like é?

Question marks mean the data was destroyed during a conversion. Sequences like é mean the bytes are intact but being decoded with the wrong character set — UTF-8 bytes read as Latin-1. The second is fully recoverable by re-decoding correctly; the first is not.

Why do filenames break when I copy files to a USB stick?

FAT32 and exFAT store filenames using a code page rather than declaring an encoding, so characters outside that page cannot be stored. Depending on the system they are either transliterated, replaced, or rejected. Formatting the drive as NTFS or exFAT with proper Unicode handling avoids most of it.

Why do Linux filenames sometimes look correct and sometimes not?

Linux filenames are raw byte sequences with no declared encoding at all — the kernel stores whatever bytes it is given. Display depends entirely on your locale settings. The same file appears correct under a UTF-8 locale and mangled under a different one, with nothing about the file having changed.

How do I fix a whole folder of broken filenames?

Use convmv on Linux or macOS, which converts filenames between encodings in bulk. Run it with --notest only after checking the dry-run output. It works when the bytes are intact and merely misinterpreted; it cannot help if the characters were replaced with question marks.

P

Written by Paras

We build free, browser-based file tools and write the reference material we wish existed when we were looking things up. Spotted an error? Tell us and we will fix it.