5

According to :help strpart(), the parameters start and len are bytes, not characters, so that enter image description here

returns a string of 2 characters, starting with the letter Beth. What I'd like to have is a function, that I can pass a hebrew (or any other multibyte character string) and not have byte semantics but character semantics, so that with the parameters 2 and 4 I am returned a four character string starting with the letter Gimmel.

Please excuse that I am posting a picture, but when I tried to copy paste my example, the left to right/right to left handling of the stackexchange-editor got into my way.

1
  • 1
    FWIW, :help string-functions and :help list-functions.
    – romainl
    Commented Oct 1, 2015 at 16:00

2 Answers 2

5

Something like this should work:

function! Strpartw(src, start, ...)
    return a:0 ?
        \ join(split(a:src, '.\zs')[a:start : (a:start + a:1 - 1)], '') :
        \ join(split(a:src, '.\zs')[a:start :], '')
endfunction
3
  • This seems to work, indeed. I am a bit shocked, though, that vim doesn't have an internal command to do that. I mean, it's a text editor, is it not? Commented Oct 1, 2015 at 15:58
  • 2
    @RenéNyffenegger You can look at it like this: Vim runs on Commodore Amiga, BeOS, VMS, and ~10 other OSes; most of those systems don't even support multi-byte characters. ;) Or like this: the Vim project has started in 1991, basic support for UTF-8 was added in version 6, and functions dealing with wide characters have been added in version 7 (about 2010). Commented Oct 1, 2015 at 16:11
  • @RenéNyffenegger That has been discussed extensively on vim_dev. The problem is, there is no backward compatible way, so it's how it is. Commented Oct 1, 2015 at 19:15
3

In lh-vim-lib, I'm doing it this way:

if exists('*strcharpart')
  function! lh#encoding#strpart(mb_string, p, l)
    " If we want a narrow contract
    " call lh#assert#value(lh#encoding#strlen(a:mb_string)).is_ge(a:p+a:l)
    return strcharpart(a:mb_string, a:p, a:l)
  endfunction
else
  function! lh#encoding#strpart(mb_string, p, l)
    " If we want a narrow contract
    " call lh#assert#value(lh#encoding#strlen(a:mb_string)).is_ge(a:p+a:l)
    return matchstr(a:mb_string, '.\{,'.a:l.'}', 0, a:p+1)
  endfunction
endif

The idea is that regex functions treat . as a character and not as a byte.

You'll also find a strlen() implementation and a at() function, but you should be able to do everything you wish with matchstr().

Note that since recent Vim versions, we can use strcharpart().

2
  • You should use '.\{,'.a:l.'}' (added comma), to make it work for "123", 0, 5 (i.e. where the length of mb_string is shorter than l).
    – blueyed
    Commented Dec 27, 2016 at 16:05
  • @blueyed. Indeed a strict compatibility with strpart() interface would require the function to work even when its user isn't using it properly. Personally, I prefer using narrow contracts and signalling programming errors instead of ignoring them: So far I don't see a valid scenario where I would provide a number of characters greater than length of the string. Commented Dec 29, 2016 at 11:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.