<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/breaking-up-lines-is-hard-to-do-lxmi6sszj" -->

---
title: Breaking up (lines) is hard to do | daily.dev
description: Python's `splitlines()` method recognizes 10 different Unicode code points (plus the CR LF sequence) as line breaks — far more than most developers expect....
canonical: https://daily.dev/posts/breaking-up-lines-is-hard-to-do-lxmi6sszj
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Breaking up (lines) is hard to do | daily.dev
og:description: Python's `splitlines()` method recognizes 10 different Unicode code points (plus the CR LF sequence) as line breaks — far more than most developers expect....
og:url: https://daily.dev/posts/breaking-up-lines-is-hard-to-do-lxmi6sszj
og:image: https://api.daily.dev/og/posts/lXmi6sSZj.png
og:image:alt: Breaking up (lines) is hard to do
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Breaking up (lines) is hard to do

**[Planet Python](https://daily.dev/sources/planetpython)** · 10 min read · 0 upvotes · 0 comments

## Summary

Python's `splitlines()` method recognizes 10 different Unicode code points (plus the CR LF sequence) as line breaks — far more than most developers expect. This deep-dive traces the historical origins of each: from ASCII's CR, LF, form feed, and vertical tab; through IBM's EBCDIC-derived NEXT LINE (U+0085); to Unicode's own LINE SEPARATOR and PARAGRAPH SEPARATOR; and finally to three ASCII information separator characters (FILE, GROUP, RECORD SEPARATOR) that qualify as line-breakers via the Unicode bidirectional algorithm's paragraph-separator class. The post explains why each character exists, which operating systems adopted which conventions, and how the Unicode Standard's technical reports (TR13, TR14, TR18, and UAX#9) collectively define the set CPython uses.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.b-list.org/weblog/2026/aug/10/newlines>

## Questions this post answers

### Why does Python's str.splitlines() split on more characters than just \n and \r\n?

Python's splitlines() recognizes eleven newline indicators total: the CR LF sequence, four code points with Unicode line-breaking property BK (line tabulation, form feed, line separator U+2028, paragraph separator U+2029), carriage return U+000D, line feed U+000A, next line U+0085, and three information separator control characters (U+001C, U+001D, U+001E) that carry bidirectional class B. This set traces back to ASCII control characters, EBCDIC compatibility, and Unicode's bidirectional algorithm and line-breaking specifications.

_Developers debugging unexpected text-splitting behavior in python can find related deep dives on daily.dev._

### What are Unicode's LINE SEPARATOR and PARAGRAPH SEPARATOR characters for?

U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR were introduced in Unicode version 5.0 via Unicode Technical Report #13 to remove ambiguity created by word processors that used newline characters to mean a paragraph break rather than a literal line break. The Unicode Standard states these characters should be used wherever the desired function is unambiguous, distinguishing line breaks from paragraph breaks explicitly.

_Anyone parsing multilingual or bidirectional text can track unicode spec nuances like this on daily.dev._

### Why do ASCII information separator control characters like U+001C count as newlines in Unicode text processing?

U+001C, U+001D, and U+001E (originally ASCII's FILE SEPARATOR, GROUP SEPARATOR, and RECORD SEPARATOR) carry Unicode bidirectional class B, the same class shared by true paragraph separators. Because the Unicode bidirectional algorithm terminates the effects of directional formatting characters at a paragraph separator, any character with bidirectional class B, including these old ASCII data-structure delimiters, functions as a line break boundary.

_Developers working through bidi text edge cases can dig into unicode internals like this on daily.dev._

## Similar posts on daily.dev

- [New lines are more than \\r and \\n](https://daily.dev/posts/new-lines-are-more-than-r-and-n-r3trz86lv) · Meziantou · 0 upvotes · 0 comments
- [Preventing line breaks in <code> elements](https://daily.dev/posts/preventing-line-breaks-in-code-elements-0yr0po6gs) · Hacker News · 0 upvotes · 0 comments
- [Embrace whitespace](https://daily.dev/posts/embrace-whitespace-lolinptvb) · Planet Python · 0 upvotes · 0 comments

---

Tags: [#python](https://daily.dev/tags/python)

[View this post on daily.dev](https://daily.dev/posts/breaking-up-lines-is-hard-to-do-lxmi6sszj)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Breaking up (lines) is hard to do","url":"https://daily.dev/posts/breaking-up-lines-is-hard-to-do-lxmi6sszj","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/breaking-up-lines-is-hard-to-do-lxmi6sszj"},"datePublished":"2026-08-11T01:50:49.683Z","dateModified":"2026-09-14T06:29:10.077Z","description":"Python's `splitlines()` method recognizes 10 different Unicode code points (plus the CR LF sequence) as line breaks — far more than most developers expect....","image":"https://media.daily.dev/image/upload/s--VDukGCjf--/f_auto/v1722860399/public/Placeholder%2002","thumbnailUrl":"https://media.daily.dev/image/upload/s--VDukGCjf--/f_auto/v1722860399/public/Placeholder%2002","isAccessibleForFree":true,"articleSection":"Planet Python","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Planet Python","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/eac3cc584cb043b5a242d13752bd3d0c","url":"https://daily.dev/sources/planetpython"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/breaking-up-lines-is-hard-to-do-lxmi6sszj","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"python","timeRequired":"PT10M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Planet Python","item":"https://daily.dev/sources/planetpython"},{"@type":"ListItem","position":3,"name":"Breaking up (lines) is hard to do"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/breaking-up-lines-is-hard-to-do-lxmi6sszj#faq","mainEntity":[{"@type":"Question","name":"Why does Python's str.splitlines() split on more characters than just \\n and \\r\\n?","acceptedAnswer":{"@type":"Answer","text":"Python's splitlines() recognizes eleven newline indicators total: the CR LF sequence, four code points with Unicode line-breaking property BK (line tabulation, form feed, line separator U+2028, paragraph separator U+2029), carriage return U+000D, line feed U+000A, next line U+0085, and three information separator control characters (U+001C, U+001D, U+001E) that carry bidirectional class B. This set traces back to ASCII control characters, EBCDIC compatibility, and Unicode's bidirectional algorithm and line-breaking specifications. Developers debugging unexpected text-splitting behavior in python can find related deep dives on daily.dev."}},{"@type":"Question","name":"What are Unicode's LINE SEPARATOR and PARAGRAPH SEPARATOR characters for?","acceptedAnswer":{"@type":"Answer","text":"U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR were introduced in Unicode version 5.0 via Unicode Technical Report #13 to remove ambiguity created by word processors that used newline characters to mean a paragraph break rather than a literal line break. The Unicode Standard states these characters should be used wherever the desired function is unambiguous, distinguishing line breaks from paragraph breaks explicitly. Anyone parsing multilingual or bidirectional text can track unicode spec nuances like this on daily.dev."}},{"@type":"Question","name":"Why do ASCII information separator control characters like U+001C count as newlines in Unicode text processing?","acceptedAnswer":{"@type":"Answer","text":"U+001C, U+001D, and U+001E (originally ASCII's FILE SEPARATOR, GROUP SEPARATOR, and RECORD SEPARATOR) carry Unicode bidirectional class B, the same class shared by true paragraph separators. Because the Unicode bidirectional algorithm terminates the effects of directional formatting characters at a paragraph separator, any character with bidirectional class B, including these old ASCII data-structure delimiters, functions as a line break boundary. Developers working through bidi text edge cases can dig into unicode internals like this on daily.dev."}}]}
```

