690 stories
·
2 followers

James Bennett: Breaking up (lines) is hard to do

1 Comment

Here’s a seemingly simple question: given a chunk of multi-line text, how do you split it and return an array whose members are the constituent lines of the text?

Hopefully, your first instinct is to reach for some sort of standard-library function, maybe something like the splitlines() method of Python’s str type. Because it turns out this “simple” question is actually pretty complex to answer! For example, quite some time ago I read a post by William Woodruff pointing out the surprising discovery that Python treats up to eleven different Unicode code points or code point sequences as indicating a line break.

At the time I meant to write about that, but a lot of other things started fighting for my time, and it’s only now that I’m finally digging it out of my drafts. Still, better late than never, so today let’s dig into some of the many ways there are to break a line of text and how they’ve been standardized and specified and ultimately wound up in the set Python uses.

In the beginning…

Once upon a time, there was ASCII. Of course there were other things before ASCII, and alongside ASCII, but for today’s discussion we really only need to go back to ASCII; if you want the full history of physical teletypes, how they evolved from typewriters and influenced character sets for computing and so on, I suggest Wikipedia. Here, I’m just going to gloss over and simplify a lot of that to focus on the topic at hand.

So. Once upon a time, there was ASCII. And it wound up being incredibly influential and important in computing, to an extent other early character sets couldn’t match. And because it was used on computers which used teletypes (basically electronic typewriters connected as input/output devices) as a user interface, it contained control characters for sending commands to the teletype. Such as a LINE FEED (byte value 0x0A) to advance the paper vertically to the next line, and a CARRIAGE RETURN (byte value 0x0D) to re-align the print head/carriage with the horizontal start point of the line.

These are often abbreviated LF and CR (or by their C-family escape sequences \n and \r, respectively), and you might think that since physically advancing a typewriter-style device to be ready to print the next line requires both operations, that would have just become the universal way everybody did new lines. Or at least the universal way everybody did them in English, or in the US, where ASCII dominated. Right?

Well, nothing is ever that simple. Physical teletypes apparently benefited from the two-character approach (as opposed to a single “new line” character) because it gave them time to physically move everything into the right position. But as virtual teletypes—“printing” to a television-like display instead of to paper—became more common, that was less of an issue. So there were multiple possible options for representing line breaks, and several of them showed up in historical systems. For example:

  • CP/M used CR LF. And so MS-DOS, which aimed for compatibility with it, used CR LF too. And so Microsoft Windows, which wanted to be compatible with MS-DOS, also used it.
  • Meanwhile, Multics chose to use just LF with no CR, and Unix went along with that choice.
  • But Commodore and Apple and many others went yet another way and used plain CR , with no LF.

This meant “plain text” was not easily portable between these various systems, since none of them could agree on how to represent a line break. Which led to one of my all-time favorite programming jokes, in the infamous NOT the comp.text.sgml FAQ document:

Q. What’s an RE?

A. RE is an acronym for Record End, which is sort of like a newline, only different. Goldfarb’s First Law of Text Processing states that:

… if a text processing system has bugs, at least one of them will have to do with the handling of input line endings.”

[The Handbook, footnote p. 321]

The Record End concept was introduced to make sure that SGML parsers don’t violate Goldfarb’s First Law.

(for the uninitiated, Charles Goldfarb created SGML)

Anyway, over twenty years ago Python tried (in Python 2.3) to smooth this over by introducing “universal newline” mode for opening files, which accepts all three options: a plain \n (Unix), or a plain \r (classic Mac), or an \r\n sequence (DOS and Windows) will all be interpreted as line breaks.

But even in ASCII there there are other ways of breaking a line. For example, at byte value 0x0C ASCII includes the FORM FEED control character (FF, or \f). Which is not one of the traditional characters used by major operating systems as a “newline”, but nonetheless does cause a new line to occur: it moves to the next page (if necessary, by ejecting the current sheet of paper from the printer and feeding in a new one). And there’s also 0x0B, VERTICAL TAB (VT or \v): just as a “regular” tab (\t) causes a horizontal adjustment, a vertical tab causes a vertical one. So it, too, causes output to advance to another line (probably skipping several in the process).

And the C1 control characters added 0x85, the NEXT LINE character (typically abbreviated NEL), useful for translating back and forth between ASCII and IBM’s EBCDIC character set (which had “New Line” as a single character).

Then Unicode happened

Today we live in a Unicode world, and Unicode tries its hardest to catalog and standardize and describe how to work with all the world’s writing systems. Chapter 5, Section 8 of the Unicode Standard, “Newline Guidelines”, lists seven code points to recognize as causing new lines. Five of them we’ve seen already:

  • U+000A LINE FEED, from ASCII
  • U+000B LINE TABULATION, from ASCII’s vertical tab
  • U+000C FORM FEED, from ASCII
  • U+000D CARRIAGE RETURN, from ASCII
  • U+0085 NEXT LINE, from the C1 control codes

The CR LF sequence is also recognized, on systems which use it.

But the other two code points are new and were created specifically for Unicode:

  • U+2028 LINE SEPARATOR (which Unicode likes to abbreviate as LS)
  • U+2029 PARAGRAPH SEPARATOR (similarly abbreviated as PS)

The Unicode Standard explains that the traditional newline characters had started to become ambiguous, because of the rise of tools such as word-processing programs which automatically wrapped lines for display and so began using explicit “newline” characters to mean a paragraph break rather than a line break. So Unicode added two new code points whose purposes are explicit. And the standard says that “[I]n Unicode text, the PS and LS characters should be used wherever the desired function is unambiguous.”

This set of line-breaking code points originated in version 5.0 of Unicode, with Unicode Technical Report #13, which lists the seven “newline” code points and the CR LF sequence. This is also the set of code points and sequences defined for line boundaries in Unicode regular expressions, Unicode Technical Standard #18.

And expanding on Chapter 5 of the Standard, there’s Unicode Standard Annex #14, “Unicode Line Breaking Algorithm”. As the name implies, this document formally specifies the line-breaking algorithm for Unicode, including defining things like which characters offer an opportunity to break a line, whether the break is mandatory, and whether the break would come before or after the character in question. It does this in a typical Unicode way: by defining a set of named properties and specifying which characters have which properties.

Two ways about it

But there are still three “newline” characters supported by Python that we haven’t seen yet, and they come from a place that might be surprising: Unicode Standard Annex #9, the bidirectional algorithm. And it’s OK if you’re wondering what that has to do with newlines, because it’s not immediately obvious if you don’t already know about it.

Some written scripts, like the Latin script this blog post is written in, are written and read left-to-right: the start of a line of text is on the left-hand side, and the end is on the right-hand side. Other scripts, such as Arabic or Hebrew, do the opposite, and are right-to-left. And so Unicode, which again wants to cover all the world’s writing systems and let you use any or all of them, has to support both left-to-right and right-to-left horizontal text direction.

But more than that, it has to support switching direction within a single piece of text. You might have something that’s in, say, Arabic but quotes something in Spanish in the middle of a line; that would require a short section of left-to-right inside an otherwise right-to-left text. Or you might be writing something that uses boustrophedon, switching directions on each line. So Unicode includes direction-control characters like U+200E LEFT-TO-RIGHT MARK and U+200F RIGHT-TO-LEFT MARK to handle this. But it also needs to know the scope of a direction change, and that’s where the last “newline” characters come in: the Unicode bidirectional algorithm says that “[t]he effects of all of these formatting characters are limited to the current paragraph; thus, they are terminated by a paragraph separator”.

So Unicode characters have, among their properties, a “bidirectional class” which influences how they affect the bidirectional algorithm. And the characters which act as paragraph separators for purposes of ending the effects of an explicit directional marker all share a common value for this: bidirectional class B. The characters with that class include quite a few that we’ve already seen, along with three more characters:

  • U+001C INFORMATION SEPARATOR FOUR
  • U+001D INFORMATION SEPARATOR THREE
  • U+001E INFORMATION SEPARATOR TWO

But these are better known by their original ASCII names: FILE SEPARATOR, GROUP SEPARATOR, and RECORD SEPARATOR. ASCII provided these to help represent data structures in memory and on storage media. Today it’s not as common to try to use control characters for this purpose, though they do have the virtue of being rare in actual text, unlike other common delimiters such as tab or comma.

End of the line

And now, after looking at multiple character sets and five Unicode technical documents, we can finally state clearly what’s going on in Python.

Python’s splitlines() treats ten different code points, and one multi-code-point sequence, as causing a line break. These are:

  • The sequence U+000D U+000A (CR LF).
  • The four code points which have line-breaking property BK (Mandatory Break (Non-tailorable)): U+000B LINE TABULATION , U+000C FORM FEED, U+2028 LINE SEPARATOR, and U+2029 PARAGRAPH SEPARATOR.
  • The one code point which has line-breaking property CR (Carriage Return (Non-tailorable)): U+000D CARRIAGE RETURN.
  • The one code point which has line-breaking property LF (Line Feed (Non-tailorable)): U+000A LINE FEED.
  • The one code point which has line-breaking property NL (Next Line (Non-tailorable)): U+0085 NEXT LINE.
  • The three code points which don’t have any of the above line-breaking properties, but do have bidirectional property B: U+001C INFORMATION SEPARATOR FOUR, U+001D INFORMATION SEPARATOR THREE, and U+001E INFORMATION SEPARATOR TWO

Which is also exactly what’s stated by a comment in the CPython source code accompanying the list of individual code points that are considered to break lines, but hopefully now you have a better understanding of what that comment means and how this particular set was arrived at.

Read the whole story
GaryBIshop
6 hours ago
reply
Wow! I was happier before I knew this.
jgbishop
3 hours ago
I need an ibuprofen after reading that.
Share this story
Delete

Track Bird Visitors With a Raspberry Pi and a USB Mic

1 Comment

Avian Visitors is a lovely project by [Teddy Warner] that uses a Raspberry Pi and microphone to keep track of which birds have been visiting your home, and creates a colorful illustration of recent visitors on top of it all.

It reports on a web interface of its own making, but what really takes things to a new level is an optional, stylish E-Ink panel that shows the last 24 hours’ worth of visitors at a glance in a collage.

The key to identification is BirdNET (GitHub here), a deep learning classifier from Cornell that can reliably identify and classify more than 11,000 species worldwide based on sound alone.

Based on that information, the system pulls bird images from a reference set for the region and creates a collage representing the breadth and frequency of visitors in a single image. The larger the image of a bird, the more frequently it was heard.

That’s a cool project, but [Teddy] took things one step further by setting up a color E-Ink display to show a running summary of all the avian visitors the system identifies. [Teddy] has a knack for leveraging projects into wall-mounted art, as we saw with his generative art wall plotter.

Got ideas of your own? Avian Visitors even has options for sending the latest detection to Home Assistant or over MQTT, allowing automation triggers based on specific bird species. If you decide to try it out and put your own spin on it, be sure to let us know by sending us a tip!

The GitHub repository for Avian Visitors has everything you need to get set up, and the basic system needs little more than a Raspberry Pi and a USB microphone. There’s a build video embedded just below, so give it a shot if you want to get a better idea of what birds come visiting.

Read the whole story
GaryBIshop
1 day ago
reply
Wow! Super cool project!
jgbishop
14 hours ago
This would be amazing to have.
Share this story
Delete

Saturday Morning Breakfast Cereal - Doc

1 Comment


Click here to go see the bonus panel!

Hovertext:
The weird part is that they're in a Burger King.


Today's News:
Read the whole story
GaryBIshop
9 days ago
reply
Ha!
Share this story
Delete

Forth

4 Comments
I NOTATION POLISH REVERSE ❤️
Read the whole story
GaryBIshop
14 days ago
reply
Forth is cool.
Share this story
Delete
3 public comments
Lythimus
14 days ago
reply
Did someone named Forth popularize suffix notation or something? I only know it as prefix, infix, and suffix.
Destrehan, LA
denismm
14 days ago
Forth is THE original stack-based programming language.
Lythimus
14 days ago
Ah, that makes sense. I completely forgot about it. I've used Common LISP a fair amount (prefix), but I haven't heard anyone mention Forth in forever.
denismm
14 days ago
We just finished discussing it in my workplace book club, making me wonder if Randall is somehow listening in on us.
JayM
14 days ago
reply
heh
Atlanta, GA
alt_text_bot
14 days ago
reply
I NOTATION POLISH REVERSE ❤️
curtisg
14 days ago
FORTH YOU LOVE IF HONK THEN
jlvanderzwan
10 days ago
We all have our petty hills to die on, one of mine is that it should be called "Łukasiewicz notation" after the mathematician who invented it, not Polish Notation because he happened to be Polish. Either that, or we should rename Feynman diagrams to American Diagrams and Penrose Tiles to English Tiles.

An ESP32 based plane radar for my desk

1 Comment

This project made for a perfect lazy Saturday unwind, after a busy week giving a talk at Devrelcon in NYC. Makerworld had this thing as one of their featured models about a week or two ago, and the parts came in while I was away.

GitHub preview for ironicbadger/ESP32-Plane-Radar GitHub repository ESP32-Plane-Radar Open-source ESP32 firmware for a 1.28″ round display that shows live ADS-B aircraft around your location as a sonar-style plane radar. by ironicbadger · C++ · MIT

Plane radar is a neat little project that turns an ESP32-C3 and a 1.28-inch round display into a live aircraft radar. It pulls nearby ADS-B traffic, plots each aircraft by distance and bearing, and shows the details directly on the screen.

It was an easy build, although it did require a little soldering. I always enjoy doing that as it reminds me of my days building racing drones. Thin, silicone based wires made quick work of the cabling. And after about 15 minutes, we were ready to go.

An ESP32-C3 wired to a round display during assembly

It is insanely easy to flash firmwares to a fresh esp32 these days using the browser-based ESPHome web flashing tool. Under 30s and you’re done.

A quick note about the 3d model

The original model was featured on Makerworld, because it looks great. Reality though was, in practice, it’s not actually that great.

ESP32 Plane Radar — Live ADS-B on a Round Display by matixovi on MakerWorld MakerWorld 5.4Klikes 3.3Kdownloads 15.1Ksaves 2.4Kmakes Original 3D model ESP32 Plane Radar Live ADS-B on a Round Display by matixovi · Published May 31, 2026

Unfortunately the tolerances are just too tight to be usable with the batch of boards I got. So I’m likely going to model my own replacement at some point, but for now I ended up printing this model instead.

ESP32-S3 1.28" Waveshare Plane Radar by ThePrintableWatch on MakerWorld MakerWorld 154likes 194downloads 470saves 102makes Original 3D model ESP32-S3 1.28" Waveshare Plane Radar by ThePrintableWatch · Published Jun 10, 2026

Customising the firmware

I’ve spent today improving my fork of ESP32 Plane Radar project, with the help of pure vibes.

The biggest improvement is proper flight context. Where the data is available, aircraft now show their origin and destination instead of just their tail number, with the callsign used as a fallback. Aircraft types are also more descriptive, something like B737-800 rather than simply B737. I added local weather, temperature, humidity, time and date as well.

The web interface can now modify coordinates after initial setup. Display options can now be changed without resetting the Wi-Fi configuration, and there are controls for units, runways, weather, temperature format and 12/24-hour time. Text is 10% larger by default, with a persistent 80–130% slider for adjusting it.

ESP32 plane radar showing live aircraft, weather and flight context

Finally, the firmware now supports authenticated OTA updates, so future builds can be installed through the browser instead of connecting the board over USB.

I’ve compiled, flashed and tested everything on the actual device, and the latest work is committed to the main branch. Next up will probably be porting it to a larger display and designing a tidy 3D-printed enclosure for it.

Adblock test (Why?)

Read the whole story
GaryBIshop
15 days ago
reply
This would look cool at your house!
Share this story
Delete

Who's afraid of Chinese models?

1 Comment

[unable to retrieve full-text content]

Comments
Read the whole story
GaryBIshop
20 days ago
reply
Really interesting read.
Share this story
Delete
Next Page of Stories