Skip to content

Commit

Permalink
String#reverse: iterate grapheme clusters for correct output when com…
Browse files Browse the repository at this point in the history
…bining characters are used
  • Loading branch information
Ary Borenszweig committed Nov 22, 2016
1 parent de680fb commit da21f0b
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 10 deletions.
7 changes: 7 additions & 0 deletions spec/std/string_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,13 @@ describe "String" do
reversed.should eq("はちいんこ")
end

it "reverses taking grapheme clusters into account" do
reversed = "noël".reverse
reversed.bytesize.should eq("noël".bytesize)
reversed.size.should eq("noël".size)
reversed.should eq("lëon")
end

describe "sub" do
it "subs char with char" do
replaced = "foobar".sub('o', 'e')
Expand Down
27 changes: 17 additions & 10 deletions src/string.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2748,18 +2748,25 @@ class String
# "racecar".reverse # => "racecar"
# ```
def reverse
String.new(bytesize) do |buffer|
buffer += bytesize
reader = Char::Reader.new(self)
reader.each do |char|
buffer -= reader.current_char_width
i = 0
char.each_byte do |byte|
buffer[i] = byte
i += 1
if ascii_only?
String.new(bytesize) do |buffer|
bytesize.times do |i|
buffer[i] = self.to_unsafe[bytesize - i - 1]
end
{@bytesize, @length}
end
else
# Iterate grpahemes to reverse the string,
# so combining characters are placed correctly
String.new(bytesize) do |buffer|
buffer += bytesize
scan(/\X/) do |match|
grapheme = match[0]
buffer -= grapheme.bytesize
buffer.copy_from(grapheme.to_unsafe, grapheme.bytesize)
end
{@bytesize, @length}
end
{@bytesize, @length}
end
end

Expand Down

0 comments on commit da21f0b

Please sign in to comment.