Blog

How Slashes and Semicolons Delimit a User Agent

User agent strings look arbitrary until the punctuation is read as structure. Two characters do most of the structural work, and a third only appears to.

The slash separates a name from a version

Within a product token, the slash is the defined separator between the product name and its version. This is the one delimiter the specification actually fixes.

A token without a slash is a bare product name with no version, which is legal. Some tokens exist purely as markers and never carry a number.

Because the slash is defined, splitting on it is safe. Everything to its left is a name, everything to its right up to the next whitespace is the version.

Whitespace separates products from each other

Products are delimited by whitespace, which is why a string reads as a run of name-slash-version pairs. This is the outer level of structure.

Whitespace inside a parenthesised comment does not separate products, because the comment belongs to the preceding token. A parser must track parenthesis depth while splitting.

Ignoring depth is the single most common parsing bug. Splitting on every space treats comment fragments as products and produces tokens that name nothing.

The semicolon is convention, not grammar

Inside comments, semicolons conventionally separate fields such as platform, architecture and device. The specification says nothing about this and imposes no meaning.

Vendors follow the convention loosely. Field counts differ, order differs between platforms, and some builds include fields that others omit entirely.

Code that splits a comment on semicolons and indexes positionally therefore works for the strings it was tested against and fails elsewhere. Position within a comment is not stable.

Parentheses can nest and must be balanced

Comments may contain nested comments, so finding the end of one means counting depth rather than searching for the next closing bracket.

Real strings rarely nest, which is why the bug survives in code for years before a string that does nest arrives. It then fails in a way that is hard to trace.

Handling depth correctly costs a few lines. It is one of the cheapest robustness improvements available to anyone writing a parser from scratch.

Reading a string by punctuation

The practical method is to strip comments first, keeping them associated with their preceding token, then split the remainder on whitespace, then split each token on its slash.

That yields a clean list of products with versions plus a set of comment blobs. What each product means is a separate question the punctuation cannot answer.

Doing the structural pass properly still leaves the interpretation problem, but it removes an entire class of errors that otherwise looks like bad data rather than bad parsing.