50

I have a String, and I would like to reverse it. For example, I am writing an AngularDart filter that reverses a string. It's just for demonstration purposes, but it made me wonder how I would reverse a string.

Example:

Hello, world

should turn into:

dlrow ,olleH

I should also consider strings with Unicode characters. For example: 'Ame\u{301}lie'

What's an easy way to reverse a string, even if it has?

2
  • Can you please provide an example where this would be useful. There are now tons of answers that don't do the right thing for many inputs because you didn't specify a use-case with strict restrictions so that reversing a string worked. None of the answers so far work with simple inputs like 'Ame\u{301}lie'. Commented Feb 3, 2014 at 23:49
  • @FlorianLoitsch I was writing an Angular filter as a demonstration. It was simple to make a filter that reversed the string. I will clarify in the question what my use case was. Commented Feb 4, 2014 at 15:03

12 Answers 12

109

The question is not well defined. Reversing arbitrary strings does not make sense and will lead to broken output. The first (surmountable) obstacle is Utf-16. Dart strings are encoded as Utf-16 and reversing just the code-units leads to invalid strings:

var input = "Music \u{1d11e} for the win"; // Music 𝄞 for the win
print(input.split('').reversed.join()); // niw eht rof

The split function explicitly warns against this problem (with an example):

Splitting with an empty string pattern ('') splits at UTF-16 code unit boundaries and not at rune boundaries[.]

There is an easy fix for this: instead of reversing the individual code-units one can reverse the runes:

var input = "Music \u{1d11e} for the win"; // Music 𝄞 for the win
print(new String.fromCharCodes(input.runes.toList().reversed)); // niw eht rof 𝄞 cisuM

But that's not all. Runes, too, can have a specific order. This second obstacle is much harder to solve. A simple example:

var input =  'Ame\u{301}lie'; // Amélie
print(new String.fromCharCodes(input.runes.toList().reversed)); // eiĺemA

Note that the accent is on the wrong character.

There are probably other languages that are even more sensitive to the order of individual runes.

If the input has severe restrictions (for example being Ascii, or Iso Latin 1) then reversing strings is technically possible. However, I haven't yet seen a single use-case where this operation made sense.

Using this question as example for showing that strings have List-like operations is not a good idea, either. Except for few use-cases, strings have to be treated with respect to a specific language, and with highly complex methods that have language-specific knowledge.

In particular native English speakers have to pay attention: strings can rarely be handled as if they were lists of single characters. In almost every other language this will lead to buggy programs. (And don't get me started on toLowerCase and toUpperCase ...).

Sign up to request clarification or add additional context in comments.

5 Comments

I learned so much from this simple Q&A that now I'm wondering what's the matter with toLowerCase and toUpperCase.
@André I think he means about assuming all languages have an upper/lower case representation of their characters... like calling toLowerCase in an emoji UTF32 code, like \u{1d11e} 𝄞
@mFeinstein It may also be referring to situations like the Turkish İ problem which lead to possibly broken comparisons depending on locale
Also Greek: a word like άσος should be ΑΣΟΣ (no diacritic) in all-caps, but Άσος (with diacritic) at the beginning of a sentence.
For the record, Dart now provides package:characters which allows you to operate on strings at the grapheme cluster level, which is what you need to not break up accents and their characters, or change complicated emojis, or even just convert "\r\n" to \n\r". Using the characters package, you can do string.characters.toList().reversed.join("") and not mangle composite characters. I still haven't found any real-world need for actually reversing a string, even if you do it correctly. And is it really correct if "👉" does not become "👈"?
24

Here's one way to reverse an ASCII String in Dart:

input.split('').reversed.join('');
  1. split the string on every character, creating an List
  2. generate an iterator that reverses a list
  3. join the list (creating a new string)

Note: this is not necessarily the fastest way to reverse a string. See other answers for alternatives.

Note: this does not properly handle all unicode strings.

6 Comments

This is not fast way because it create List<String>. If you have string with big length it create to many strings. One string per each code unit in input string. This is not efficient way.
Mezoni is right: his solution is simpler and faster.
Thanks. I'll clarify that my answer is not intended to be the fastest way.
@FlorianLoitsch I tested 'Ame\u{301}lie' with the above and it was reversed. What are some strings where this fails? print('Ame\u{301}lie'.split('').reversed.join('')); ==> eiĺemA
That's not the correct inverse. The accent is on the "l" and not on the "e". Also: this is just an example where the runes need to be in order. If you have surrogate pairs it's even worse: "Music \u{1d11e} for the win".
|
14

I've made a small benchmark for a few different alternatives:

String reverse0(String s) {
  return s.split('').reversed.join('');
}

String reverse1(String s) {
  var sb = new StringBuffer();
  for(var i = s.length - 1; i >= 0; --i) {
    sb.write(s[i]);
  }
  return sb.toString();
}

String reverse2(String s) {
  return new String.fromCharCodes(s.codeUnits.reversed);
}

String reverse3(String s) {
  var sb = new StringBuffer();
  for(var i = s.length - 1; i >= 0; --i) {
    sb.writeCharCode(s.codeUnitAt(i));
  }
  return sb.toString();
}

String reverse4(String s) {
  var sb = new StringBuffer();

  var i = s.length - 1;

  while (i >= 3) {
    sb.writeCharCode(s.codeUnitAt(i-0));
    sb.writeCharCode(s.codeUnitAt(i-1));
    sb.writeCharCode(s.codeUnitAt(i-2));
    sb.writeCharCode(s.codeUnitAt(i-3));
    i -= 4;
  }

  while (i >= 0) {
    sb.writeCharCode(s.codeUnitAt(i));
    i -= 1;
  }

  return sb.toString();
}

String reverse5(String s) {
  var length = s.length;
  var charCodes = new List(length);
  for(var index = 0; index < length; index++) {
    charCodes[index] = s.codeUnitAt(length - index - 1);
  }

  return new String.fromCharCodes(charCodes);
}
main() {
  var s = "Lorem Ipsum is simply dummy text of the printing and typesetting industry.";

  time('reverse0', () => reverse0(s));
  time('reverse1', () => reverse1(s));
  time('reverse2', () => reverse2(s));
  time('reverse3', () => reverse3(s));
  time('reverse4', () => reverse4(s));
  time('reverse5', () => reverse5(s));
}

Here is the result:

reverse0: => 331,394 ops/sec (3 us) stdev(0.01363)
reverse1: => 346,822 ops/sec (3 us) stdev(0.00885)
reverse2: => 490,821 ops/sec (2 us) stdev(0.0338)
reverse3: => 873,636 ops/sec (1 us) stdev(0.03972)
reverse4: => 893,953 ops/sec (1 us) stdev(0.04089)
reverse5: => 2,624,282 ops/sec (0 us) stdev(0.11828)

3 Comments

Not a major addition, but a decreasing for loop rather than an increasing one in the last example should in theory make it faster
Last one is buggy. But the benchmarks presents usefull information.
Sweet! The time() method? Is that from some kind of package?
3

A fairly simple way (without taking into account the sorting direction) is to use information about Unicode mapping sequences.

import 'package:sequence_processor/sequence_processor.dart';
import 'package:unicode/decomposers/canonical.dart';
import 'package:unicode/emoji/emoji.dart';

void main(List<String> args) {
  var str = 'hello🇵🇬你们😀😀👨‍👩‍👦';
  print('"$str"');
  print(_reverse(str));

  str = 'I 💗 you! ❤️‍🔥 ';
  print('"$str"');
  print(_reverse(str));

  str = 'Amélie';
  print(str);
  print('${_reverse(str)} <= it seems to work');

  print(
      '${String.fromCharCodes(str.runes.toList().reversed)} <= does not work');
}

String _reverse(String text) {
  if (text.isEmpty) {
    return '';
  }

  final result = _processor.process(text.runes.toList());
  if (result.isEmpty) {
    return '';
  }

  return result.reversed
      .map((e) => e.data == null
          ? String.fromCharCode(e.element!)
          : String.fromCharCodes(e.sequence!))
      .join();
}

// Should be stored in a static member for performance reasons.
final _processor = () {
  final emojis = getUnicodeEmojiList();
  final processor = SequenceProcessor<int, Object>();
  for (final emoji in emojis) {
    processor.addSequence(emoji.sequence, emoji);
  }

  const decomposer = CanonicalDecomposer();
  final mappingList = decomposer.getMappingList();
  for (var i = 0; i < mappingList.length; i++) {
    final mapping = mappingList[i];
    final sequence = mapping.$2;
    if (sequence.length > 1) {
      if (!processor.hasSequence(sequence)) {
        processor.addSequence(mapping.$2, mapping.$1);
      }
    }
  }

  return processor;
}();

Output:

"hello🇵🇬你们😀😀👨‍👩‍👦"
👨‍👩‍👦😀😀们你🇵🇬olleh
"I 💗 you! ❤️‍🔥 "
 ❤️‍🔥 !uoy 💗 I
Amélie
eilémA <= it seems to work
eiĺemA <= does not work

1 Comment

This is broken for the reasons described in the higher-ranked answers.
2

Try this function

String reverse(String s) {
  var chars = s.splitChars();
  var len   = s.length - 1;
  var i     = 0;

  while (i < len) {
    var tmp = chars[i];
    chars[i] = chars[len];
    chars[len] = tmp;
    i++;
    len--;
  }

  return Strings.concatAll(chars);
}

void main() {
  var s = "Hello , world";
  print(s);
  print(reverse(s));
}

(or)

String reverse(String s) {
  StringBuffer sb=new StringBuffer();
  for(int i=s.length-1;i>=0;i--) {
    sb.add(s[i]);
  }
  return sb.toString();
}

main() {
  print(reverse('Hello , world'));
}

1 Comment

This is broken for the reasons described in the higher-ranked answers.
2

The library More Dart contains a light-weight wrapper around strings that makes them behave like an immutable list of characters:

import 'package:more/iterable.dart';

void main() {
  print(string('Hello World').reversed.join());
}

Comments

2

There is a utils package that covers this function. It has some more nice methods for operation on strings.

Install it with :

dependencies:
  basic_utils: ^1.2.0

Usage :

String reversed = StringUtils.reverse("helloworld");

Github:

https://github.com/Ephenodrom/Dart-Basic-Utils

1 Comment

Does this work for all edge cases as described in the higher-ranking answers?
1

Create this extension:

extension Ex on String {
  String get reverse => split('').reversed.join();
}

Usage:

void main() {
  String string = 'Hello World';
  print(string.reverse); // dlroW olleH
}

Comments

1

Reversing "Hello World"

2 Comments

While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
0

Here is a function you can use to reverse strings. It takes an string as input and will use a dart package called Characters to extract characters from the given string. Then we can reverse them and join again to make the reversed string.

String reverse(String string) {
  if (string.length < 2) {
    return string;
  }

  final characters = Characters(string);
  return characters.toList().reversed.join();
}

Comments

0

** Here is a function you can use to reverse strings. It takes an string as input and will use a dart package called split() to extract characters from the given string. Then we can reverse them and join again to make the reversed string. **

 String? stringReverse(String? string) {
      if (string != null) {
        final output = string.split('').reversed.join();
        return output;
      }
    }

    void main() {
      String? string = stdin.readLineSync();
      print(stringReverse(string));
    }

Comments

0

With StringBuffer:

String firstReverse(String str) {
      StringBuffer reversedStr = StringBuffer();
    
      for (int i = str.length - 1; i >= 0; i--) {
        reversedStr.write(str[i]);
      }
    
      return reversedStr.toString();
    }

With CodeUnits:

String firstReverse(String str) {
  var stringToNumList = [];
  String reverseStr = '';

  for (var i = 0; i < str.length; i++) {
    stringToNumList.add(str[i].codeUnitAt(0));
  }

  int start = 0, last = stringToNumList.length - 1;

  while (start < last) {
    var temp = stringToNumList[start];
    stringToNumList[start] = stringToNumList[last];
    stringToNumList[last] = temp;
    start++;
    last--;
  }

  for (var i = 0; i < stringToNumList.length; i++) {
    reverseStr += String.fromCharCode(stringToNumList[i]);
  }

  return reverseStr;
}

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.