4

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
    FWIW, :help string-functions and :help list-functions. – romainl Oct 1 '15 at 16:00
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
  • 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? – René Nyffenegger Oct 1 '15 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). – Sato Katsura Oct 1 '15 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. – Christian Brabandt Oct 1 '15 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().

  • 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 Dec 27 '16 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. – Luc Hermitte Dec 29 '16 at 11:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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