# Substring function?

**URL:** https://discourse.julialang.org/t/substring-function/76675
**Category:** New to Julia
**Tags:** strings, unicode
**Created:** [February 18, 2022, 7:41am UTC](https://discourse.julialang.org/t/substring-function/76675 "2022-02-18T07:41:49Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![rogerkeays](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rogerkeays/32/33927_2.png) [@rogerkeays](https://discourse.julialang.org/u/rogerkeays)
#### Post date: [February 18, 2022, 7:41am UTC](https://discourse.julialang.org/t/substring-function/76675/1 "2022-02-18T07:41:49Z")

</div>

I found `first(str)`, `last(str`), and `chop(str)`, but couldn’t find anything for getting a substring where multibyte unicode characters are involved. Something like:

```julia
substring(str, start, stop) = str[nextind(str, 0, start):nextind(str, 0, stop)]

```

Not sure if anyone else found this surprising. I suppose you could use `chop`, but my guess is `substring` is more common.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 18, 2022, 7:58am UTC](https://discourse.julialang.org/t/substring-function/76675/2 "2022-02-18T07:58:49Z")

</div>

You may wanna check this [related thread](https://discourse.julialang.org/t/indexing-strings-by-unicode-code-point-instead-of-code-unit/55248).

---

<div class="post-metadata">

### Author: ![rogerkeays](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rogerkeays/32/33927_2.png) [@rogerkeays](https://discourse.julialang.org/u/rogerkeays)
#### Post date: [February 18, 2022, 8:04am UTC](https://discourse.julialang.org/t/substring-function/76675/3 "2022-02-18T08:04:10Z")

</div>

Yeh, been there. Just thought a `substring` function would be useful out of the box, with documentation about how it differs from the `string[start:stop]` form in terms of unicode and performance.

---

<div class="post-metadata">

### Author: ![fredrikekre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fredrikekre/32/1688_2.png) [@fredrikekre](https://discourse.julialang.org/u/fredrikekre)
#### Post date: [February 18, 2022, 8:21am UTC](https://discourse.julialang.org/t/substring-function/76675/4 "2022-02-18T08:21:24Z")

</div>

You can use the [`SubString`](https://docs.julialang.org/en/v1/base/strings/#Base.SubString) constructor directly.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 18, 2022, 8:37am UTC](https://discourse.julialang.org/t/substring-function/76675/5 "2022-02-18T08:37:23Z")

</div>

> [@fredrikekre](#):
>
> You can use the [`SubString`](https://docs.julialang.org/en/v1/base/strings/#Base.SubString) constructor directly

Which btw, is in one of Steve’s responses in the thread linked - [here](https://discourse.julialang.org/t/indexing-strings-by-unicode-code-point-instead-of-code-unit/55248/12).

---

<div class="post-metadata">

### Author: ![rogerkeays](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rogerkeays/32/33927_2.png) [@rogerkeays](https://discourse.julialang.org/u/rogerkeays)
#### Post date: [February 18, 2022, 8:39am UTC](https://discourse.julialang.org/t/substring-function/76675/6 "2022-02-18T08:39:19Z")

</div>

Unless I’m reading the docs incorrectly, `SubString` uses byte indexes.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 18, 2022, 9:05am UTC](https://discourse.julialang.org/t/substring-function/76675/7 "2022-02-18T09:05:13Z")

</div>

Are you looking for a built-in function that outputs as in example below?

```julia
substring(s,n) = join([s[c] for (i,c) in enumerate(eachindex(s)) if i ∈ n])

```

> **Results**
>
> ```julia
> s = "αβγ"
> substring(s,1) # 'α'
> substring(s,2) # 'β'
> substring(s,3) # 'γ'
> substring(s,1:2) # "αβ"
> 
> ```

---

<div class="post-metadata">

### Author: ![rogerkeays](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rogerkeays/32/33927_2.png) [@rogerkeays](https://discourse.julialang.org/u/rogerkeays)
#### Post date: [February 18, 2022, 9:42am UTC](https://discourse.julialang.org/t/substring-function/76675/8 "2022-02-18T09:42:18Z")

</div>

Your implementation is interesting, but a little inefficient. Compare:

```julia
substring(s,n) = join([s[c] for (i,c) in enumerate(eachindex(s)) if i ∈ n])
substring(str, start, stop) = str[nextind(str, 0, start):nextind(str, 0, stop)]

```

and after they’ve both been warmed up…

```julia
julia> s = "αβγł€đŧŧŋ"
"αβγł€đŧŧŋ"
julia> @time substring(s,1:5)
  0.000025 seconds (7 allocations: 400 bytes)
"αβγł€"

julia> @time substring(s,1,5)
  0.000007 seconds (1 allocation: 32 bytes)
"αβγł€"

```

I’m mostly concerned about memory usage here. Still, pretty cool.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 18, 2022, 9:55am UTC](https://discourse.julialang.org/t/substring-function/76675/9 "2022-02-18T09:55:02Z")

</div>

You can get 0-allocations by using a view:

```julia
substring(str, start, stop) = view(str, nextind(str, 0, start):nextind(str, 0, stop))

```

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [February 18, 2022, 2:42pm UTC](https://discourse.julialang.org/t/substring-function/76675/10 "2022-02-18T14:42:46Z")

</div>

> Just thought a `substring` function would be useful out of the box, with documentation about how it differs from the `string[start:stop]` form in terms of unicode and performance.

Slicing `a[m:n]` _always_ makes a copy in Julia (at least, with the built-in types), whether for arrays or strings. If you want to use a view (i.e. create a `SubString` object), the easiest way is to [use `@views` on a block of code](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-views), e.g.

```julia
julia> s = "αβγł€đŧŧŋ"
"αβγł€đŧŧŋ"

julia> @views s[1:5]
"αβγ"

julia> typeof(ans)
SubString{String}

```

> [@rogerkeays](#):
>
> getting a substring where multibyte unicode characters are involved

Slicing with `@views` works just fine for this.

The real question is, where are you getting these character indices that you want to pass to your `substring` function? Usually you get indices to a substring from some previous iteration over the string, either from your own loop or from something like a `findnext` call, and these give you codeunit indices that you can pass to `s[m:n]` directly.

If you are counting codepoints as “characters”, e.g. you want the “first 3 characters” in a string, then the odds are high that you are making a mistake. For example, `"ü"` is _two_ codepoints (`length("ü") == 2`) because it is `u` followed by a [combining character](https://en.wikipedia.org/wiki/Combining_character) [U+0308](https://www.fileformat.info/info/unicode/char/0308/index.htm). See also this explanation: [Myth: Counting coded characters or code points is important.](http://utf8everywhere.org/#myth.strlen)

Because of Unicode’s complexity, wanting a substring from the `m`-th _codepoint_ (“character”) to the `n`-th codepoint, as opposed to between two string _indices_ (= code units), is actually an extremely uncommon operation (in non-buggy code). This is why it’s not built-in.

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [February 18, 2022, 3:41pm UTC](https://discourse.julialang.org/t/substring-function/76675/11 "2022-02-18T15:41:02Z")

</div>

Maybe you will find this useful [Subsetting strings in Julia using character indexing | Blog by Bogumił Kamiński](https://bkamins.github.io/julialang/2020/08/29/charindex.html)

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [February 18, 2022, 3:51pm UTC](https://discourse.julialang.org/t/substring-function/76675/12 "2022-02-18T15:51:59Z")

</div>

> [@bkamins](#):
>
> Maybe you will find this useful […]

It’s fun to write macros like this, but I would add a warning that probably 99% of the time people do character indexing they are making a mistake in their Unicode handling.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 18, 2022, 4:01pm UTC](https://discourse.julialang.org/t/substring-function/76675/13 "2022-02-18T16:01:39Z")

</div>

So at the end of the races, what would be a simple function to perform character indexing of a unicode string?

Say a string like: `s = "αβüγ"`, where I see 4 characters, but I am not sure anymore!

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [February 18, 2022, 4:12pm UTC](https://discourse.julialang.org/t/substring-function/76675/14 "2022-02-18T16:12:16Z")

</div>

The solution in my blog does this. What @stevengj says, if I understand him correctly, is that doing character indexing is not a safe practice in general.

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 18, 2022, 4:31pm UTC](https://discourse.julialang.org/t/substring-function/76675/15 "2022-02-18T16:31:18Z")

</div>

Thanks Bogumil, but I only saw a macro. Is there a function too?

Regarding:

> [@bkamins](#):
>
> character indexing is not a safe practice in general.

Smoking neither, but there are 1 billion people who have chosen to do so…

---

<div class="post-metadata">

### Author: ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)
#### Post date: [February 18, 2022, 4:42pm UTC](https://discourse.julialang.org/t/substring-function/76675/16 "2022-02-18T16:42:39Z")

</div>

You can write a similar version as function. I used macro as it then can take advantage of indexing syntax.

---

<div class="post-metadata">

### Author: ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)
#### Post date: [February 18, 2022, 4:51pm UTC](https://discourse.julialang.org/t/substring-function/76675/17 "2022-02-18T16:51:58Z")

</div>

It depends on what you mean by character! Do you mean code points? Or do you mean grapheme clusters?

---

<div class="post-metadata">

### Author: ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)
#### Post date: [February 18, 2022, 5:11pm UTC](https://discourse.julialang.org/t/substring-function/76675/18 "2022-02-18T17:11:00Z")

</div>

Honestly, I have no clue and had to search.  
I guess it is graphemes (_user-perceived characters_ in unicode) as per [solution posted here](https://discourse.julialang.org/t/how-to-iterate-over-unicode-characters-with-multiple-codepoints/47828/5).

---

<div class="post-metadata">

### Author: ![rogerkeays](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rogerkeays/32/33927_2.png) [@rogerkeays](https://discourse.julialang.org/u/rogerkeays)
#### Post date: [February 19, 2022, 4:48am UTC](https://discourse.julialang.org/t/substring-function/76675/19 "2022-02-19T04:48:53Z")

</div>

All of these arguments about byte indexing apply equally to `first`, `last`, and `chop`, which are part of the standard library and index on unicode codepoints.

From the julia source:

```julia
first(s::AbstractString, n::Integer) = @inbounds s[1:min(end, nextind(s, 0, n))]
last(s::AbstractString, n::Integer) = @inbounds s[max(1, prevind(s, ncodeunits(s)+1, n)):end]
function chop(s::AbstractString; head::Integer = 0, tail::Integer = 1)
    if isempty(s)
        return SubString(s)
    end
    SubString(s, nextind(s, firstindex(s), head), prevind(s, lastindex(s), tail))
end

```

Guess I’ll just have to start my own `util` package like you do in Java 🤣

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [February 19, 2022, 4:59am UTC](https://discourse.julialang.org/t/substring-function/76675/20 "2022-02-19T04:59:35Z")

</div>

> [@rogerkeays](#):
>
> All of these arguments about byte indexing apply equally to `first` , `last` , and `chop` , which are part of the standard library and index on unicode codepoints.

`first` and `last` are defined for _any_ iterator. Since string iteration is over codepoints, they have to be consistent, but I agree that they need to be used with care.

As for `chop`, as far as I can tell it’s used to chop off a _known_ suffix (usually an ASCII suffix so there are no issues with Unicode normalization), like a file extension, which is safe enough. (However, in [starting in Julia 1.8](https://github.com/JuliaLang/julia/pull/40995) it will often be better to use the new `chopprefix` and `chopsuffix` functions, which only remove the prefix/suffix if it is present and which may be more efficient because they can avoid decoding the UTF-8.)

[Next page](https://discourse.julialang.org/t/substring-function/76675.md?page=2)
