pm100

pm100

\r\n as a grapheme is it 1 or 2?

I am assuming this is a Windows thing. Regex seems to treat \r\n as 2 characters but String treats them as one

trying to strip whitespace off the front of a string

iex(17)> regex = ~r/^\s+/
~r/^\s+/
iex(18)> junk = "\r\n\r\nabc"
"\r\n\r\nabc"
iex(19)> Regex.run(regex, junk, return: :index)
[{0, 4}]
iex(20)> String.slice(junk,  4..-1//1)
"c"
iex(21)> String.graphemes(juk)
["\r\n", "\r\n", "a", "b", "c"]
iex(22)>

Am I reading this correctly (absolute elixir noob). What the correct way to fix it?

Marked As Solved

sabiwara

sabiwara

Elixir Core Team

As the Regex docs are mentioning, :index “returns byte index and match length”, not the grapheme index.

This works:

iex> binary_slice("\r\n\r\nabc", 4..-1//1)
"abc"

Bytes != codepoints != graphemes.

iex> junk = "\r\n\r\nabc é"
"\r\n\r\nabc é"
iex> for <<byte <- junk>>, do: <<byte>>
["\r", "\n", "\r", "\n", "a", "b", "c", " ", <<195>>, <<169>>]
iex> String.codepoints(junk)
["\r", "\n", "\r", "\n", "a", "b", "c", " ", "é"]
iex> String.graphemes(junk)
["\r\n", "\r\n", "a", "b", "c", " ", "é"]

The String documentation explains this topic well.

Also Liked

sabiwara

sabiwara

Elixir Core Team

String is basically a set of helper functions for UTF8 binaries

Exactly, it is the same underlying structure (binaries containing valid UTF8) but what changes is if you chose to process it as bytes, codepoints (UTF-8 characters) or graphemes (groups of codepoints which will be displayed as a single character to the human reader).

However this does not change a thing that only String sees \r\n as a single character.

This depends of the function. Many functions of the String module are explicitly working with graphemes, such as String.length, String.at, String.first
String.codepoints, String.next_codepoints, String.to_charlist, ::utf8 in bitstring patterns all work with codepoints.

It’s a serious gotcha that 2 UTF8 characters are seen as 1

Graphemes and codepoints are arguably a gotcha in any language, so I’m not disagreeing, but I wouldn’t pin it on the String library :sweat_smile:

LostKobrakai

LostKobrakai

No. Graphemes are a group construct. Sometimes a grapheme is just a single codepoint. Sometimes a grapheme consists of multiple codepoints – depending on how those codepoints follow up on each other.

iex(1)> str = "🏴‍☠️"
"🏴\u200D☠️"
iex(2)> String.graphemes(str)
["🏴\u200D☠️"]
iex(3)> String.codepoints(str)
["🏴", "\u200D", "☠", "️"]
iex(4)> str = "🏴"
"🏴"
iex(5)> String.graphemes(str)
["🏴"]
iex(6)> String.codepoints(str)
["🏴"]

And if you forget about “the computer in the back” \r\n and \n look the exact same to someone in notepad. It’s just us programmers still dealing with the fact that all this came from a typewrighter once. To everyone else it’s a newline no matter what.

adamu

adamu

FYI flags are multiple codepoints too.

They are the country code offset by 127397.

iex> for <<cp::utf8 <- "UN">>, do: <<(cp +  127397)::utf8>>, into: ""
"🇺🇳"

Where Next?

Popular in Questions Top

Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
makeitrein
Hey all, just started picking up Elixir last week and am writing a scraper as a learning project. Baby step #1 is extracting the number ...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

We're in Beta

About us Mission Statement