Course outline · 0% complete

0/28 lessons0%

Course overview →

What a file really is

lesson 7-1 · ~11 min · 21/28

From lesson 2-3, a text file containing café occupies 5 bytes on disk in UTF-8, because é takes two bytes.

Disks store bytes, never characters. UTF-8 packs c, a, and f into one byte each and é into two, so the file is 5 bytes long even though the text is 4 characters.

This lesson is about what a file actually is, which turns out to be exactly such a sequence of bytes, plus a name and some bookkeeping.

A file is a named bag of bytes

Strip away the icons and extensions and a file is just two things:

  1. a sequence of bytes on disk, which is the content
  2. metadata the filesystem keeps about it: name, size, owner, permissions, timestamps

That is all. A .txt, a .jpg, and a .py are the same kind of thing.

PartExample
contentthe bytes 48 69 21
metadataname greeting.txt, size 3, owner ada

The extension is a hint for humans and applications about how to interpret the bytes, which is unit 2's point that bits mean nothing without an agreed rule. Renaming photo.jpg to photo.txt changes zero bytes of content.

The filesystem is the part of the OS that turns "the bytes of /Users/ada/notes.txt" into actual locations on the disk hardware, and it does that bookkeeping for millions of files at once.

4869210acontent: just bytesthe extension is a hint, not a factname: greeting.txtsize: 4owner: adapermissions, timestampsrenaming edits the record above, and touches zero content bytesmetadata record
A file is content bytes plus a separate metadata record, which is why renaming changes nothing inside.

Every byte accounted for

Two lines written, read back, and counted.

with open("notes.txt", "w") as f:
    f.write("first line\n")
    f.write("second line\n")

with open("notes.txt", "r") as f:
    content = f.read()

print(content, end="")
print("characters on disk:", len(content))

Output

first line
second line
characters on disk: 23

The \n newline characters are real bytes in the file and they count. "first line\n" is 11 characters and "second line\n" is 12, which gives 23.

There is no invisible formatting in a file. A blank line is a newline byte, indentation is space or tab bytes, and the reason a file looks like lines at all is that a program chose to split on those bytes.

Writing three numbers and reading back text

Binary mode bypasses text decoding entirely.

with open("raw.bin", "wb") as f:
    f.write(bytes([72, 105, 33]))

with open("raw.bin", "rb") as f:
    data = f.read()

print(data)
print(data.decode("utf-8"))

Output

b'Hi!'
Hi!

The modes "wb" and "rb" skip all text decoding and hand over raw bytes. The numbers 72, 105, and 33 are the codes for H, i, and ! from lesson 2-3.

Three numbers went in and text came out, because text is numbers. The .decode("utf-8") call is the only place any interpretation happened, and choosing a different encoding would have produced different characters from the identical bytes.

Renaming a PDF to a PNG

Renaming report.pdf to report.png leaves the content bytes unchanged and edits only the name metadata, so image viewers will fail to open it.

The bytes are still a PDF, and a PNG viewer rejects them because the content does not follow PNG's byte format. Renaming it back makes everything work again, since nothing was lost.

OperationTouches contentTouches metadata
renamenoyes
writeyesyes, the size and timestamp
convert with a toolyes, new bytes entirelyyes

This is also how file-type detection works in practice. Tools such as file ignore the extension and read the first few bytes, called a magic number, because the content is the only trustworthy evidence of what a file is.