18 comments

[ 5.3 ms ] story [ 799 ms ] thread
This article provides no additional value to the splitlines() docs.
For more controlled splitting, I really like Unicode named characters classes[0] for more precise splitting and matching tasks.

[0]: https://en.wikipedia.org/wiki/Unicode_character_property#Gen...

Given that encoded characters must have one and only one General_Category value, it might be too imprecise or arbitrary in some cases. If you ever need more power, it's worth browsing the other character properties Unicode exposes. For example, `Lu` (Uppercase_Letter) only covers some uppercase letters, whereas the `Uppercase` property covers all of them.

---

For anyone that wants to learn more about specific Unicode stuff, the three big data sources are The Core Spec, the Unicode Technical Annexes (UAXs), and the Unicode Character Database itself (the database is a bunch of text files. There's an XML version now as well).

For further reading on this specifically, it might be worth looking at:

[Unicode Core Spec - Chapter 4: Character Properties] https://www.unicode.org/versions/Unicode17.0.0/core-spec/cha...

├ [General Category] https://www.unicode.org/versions/Unicode17.0.0/core-spec/cha...

└ [Properties for Text Boundaries] https://www.unicode.org/versions/Unicode17.0.0/core-spec/cha...

[UAX #44 - Unicode Character Database (Technical Report)] https://www.unicode.org/reports/tr44/

├ [General Category Values] https://www.unicode.org/reports/tr44/#General_Category_Value...

└ [Property Definitions] https://www.unicode.org/reports/tr44/#Property_Definitions

And, if you're brave and want to see the data itself (skim through UAX #44 first):

[Unicode Character Database] https://www.unicode.org/Public/17.0.0/ucd/

What, no <br\s*\/?>?
Splitlines is generally not needed. for line in file: is more idiomatic.
It has similar (but not identical) behaviour though:

  >>> for line in StringIO("foo\x85bar\vquux\u2028zoot"): print(line)
  ... 
  foo
  bar
   quux zoot
I think you're getting fooled:

  >>> from io import StringIO
  >>> for line in StringIO("foo\x85bar\vquux\u2028zoot"): print(repr(line))
  ... 
  'foo\x85bar\x0bquux\u2028zoot'
If it's reading from a file, you wouldn't be using splitlines() anyway; you'd use readlines().

For string you’d need to

  import io

  for line in io.StringIO(str):
    pass
str.split() function does the same:

>>> s = "line1\nline2\rline3\r\nline4\vline5\x1dhello"

>>> s.split() ['line1', 'line2', 'line3', 'line4', 'line5', 'hello']

>>> s.splitlines() ['line1', 'line2', 'line3', 'line4', 'line5', 'hello']

But split() has sep argument to define delimiter according which to split the string.. In which case it provides what you expected to happen:

>>> s.split('\n') ['line1', 'line2\rline3\r', 'line4\x0bline5\x1dhello']

In general you want this:

>>> linesep_splitter = re.compile(r'\n|\r\n?')

>>> linesep_splitter.split(s) ['line1', 'line2', 'line3', 'line4\x0bline5\x1dhello']

splitlines() is sometimes nice for adhoc parsing (of well behaved stuff...) because it throws out whitespace-only lines from the resulting list of strings.

#1 use-case of that for me is probably just avoiding the cases where there's a trailing newline character in the output of a command I ran by subprocess.

In that example str.split() has the same result as str.splitlines(), but it's not in general the same, even without custom delimiter.

str.split() splits on runs of consecutive whitespace, any type of whitespace, including tabs and spaces which splitlines() doesn't do.

    >>> 'one two'.split()
    ['one', 'two']
    >>> 'one two'.splitlines()
    ['one two']
split() without custom delimiter also splits on runs of whitespace, which splitline() also doesn't do (except for \r\n because that combination counts as one line ending):

    >>> 'one\n\ntwo'.split()
    ['one', 'two']
    >>> 'one\n\ntwo'.splitlines()
    ['one', '', 'two']
in the same theme, NTLAIL strip(), rstrip(), lstrip() can strip other kinds of characters besides whitespace.
One thing to note tho is that they take character sets, as long as they encounter characters in the specified set they will keep stripping. Lots of people think if you give it a string it will remove that string.

That feature was added in 3.9 with the addition of `removeprefix` and `removesuffix`.

Sadly,

1. unlike Rust's version they provide no way of knowing whether they stripped things out

2. unlike startswith/endswith they do not take tuples of prefixes/suffixes

Useful to know for security purposes, surprises like that might cause vulnerabilities..
(comment deleted)
Is there a parser ambiguity/confusion vector here?