在Dart中如何反转一个字符串?

43

我有一个字符串,想要将其反转。例如,我正在编写一个AngularDart过滤器,用于反转字符串。这只是演示目的,但它让我想知道如何反转字符串。

示例:

Hello, world

应该变成:

dlrow ,olleH

我也应该考虑带有Unicode字符的字符串。例如:'Ame\u{301}lie'

如果一个字符串包含Unicode字符,那么有什么简单的方法可以翻转它呢?


请您提供一个使用此功能的示例,这将非常有用。现在有很多答案不能正确处理许多输入,因为您没有指定具有严格限制的用例,以便反转字符串可以正常工作。到目前为止,所有答案都无法处理简单的输入,例如'Ame\u{301}lie' - Florian Loitsch
@FlorianLoitsch 我正在编写一个Angular过滤器作为演示。制作一个反转字符串的过滤器非常简单。我将在问题中澄清我的用例。 - Seth Ladd
10个回答

102

这个问题的定义不够清晰。翻转任意字符串没有意义,会导致破损的输出。第一个(可克服的)障碍是Utf-16。Dart字符串被编码为Utf-16,仅翻转代码单元会导致无效的字符串:

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

split函数明确警告此问题(并给出了示例):

使用空字符串模式('')进行拆分会在UTF-16码元边界而非rune边界处拆分。

这个问题有一个简单的解决方法:不要翻转单个码元,而是翻转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

请注意,重音落在错误的字符上。

可能有其他语言更加敏感于单个符文的顺序。

如果输入有严格的限制(例如Ascii或Iso Latin 1),则技术上可以反转字符串。然而,我还没有看到过一个单一用例,这种操作是有意义的。

将此问题用作展示字符串具有类似列表的操作不是一个好主意。除了少数用例外,必须针对特定语言进行处理字符串,并使用具有语言特定知识的高度复杂的方法。

特别是以英语为母语的人必须注意:几乎不能将字符串处理为单个字符的列表。在几乎所有其他语言中,这将导致程序出错。(而且不要让我开始讨论 toLowerCase 和 toUpperCase ...)。


1
我从这个简单的问答中学到了很多,现在我想知道 toLowerCasetoUpperCase 到底有什么问题。 - André
@André 我认为他的意思是假设所有语言都有其字符的大写/小写表示...就像在表情符号UTF32代码中调用 toLowerCase,例如 \u{1d11e} - Michel Feinstein
2
@mFeinstein 这也可能涉及到像Turkish İ问题这样的情况,根据语言环境的不同可能会导致比较出现错误。 - Salem
2
同时,希腊语中像“άσος”这样的单词在全大写时应该是“ΑΣΟΣ”(没有音标),但在句子开头时应该是带有音标的“Άσος”。 - Lionel Rowe
2
值得一提的是,Dart现在提供了package:characters,它允许您在字形簇级别上操作字符串,这正是您需要的,以避免打断重音和它们的字符,或更改复杂的表情符号,甚至只是将"\r\n"转换为\n\r"。使用characters包,您可以执行string.characters.toList().reversed.join("")而不会弄乱组合字符。即使您正确地执行了反转字符串,我仍然没有找到任何实际需要反转字符串的情况。如果""不变成"",那么它是否真正正确呢? - lrn

23

这是在Dart中反转一个ASCII字符串的一种方法:

input.split('').reversed.join('');
  1. 在每个字符上拆分字符串,创建一个列表
  2. 生成一个迭代器来反转列表
  3. 连接该列表(创建一个新字符串)

注意:这并不一定是最快反转字符串的方法。有其他方案可以参考。

注意:这种方法不能正确处理所有Unicode字符串。


这不是一种快速的方法,因为它创建了List<String>。如果您有一个长度很大的字符串,它会创建太多的字符串。每个输入字符串中的代码单元都对应一个字符串,这不是一种高效的方式。 - mezoni
Mezoni是正确的:他的解决方案更简单、更快。 - Florian Loitsch
谢谢。我要澄清的是,我的回答并不打算是最快的方法。 - Seth Ladd
1
@FlorianLoitsch 我用上述方法测试了“Amélie”,结果被反转了。还有哪些字符串会出现这种情况?print('Ame\u{301}lie'.split('').reversed.join('')); ==> eiĺemA - Seth Ladd
1
那不是正确的反向。 重音在“l”上,而不是“e”上。 另外:这只是一个需要符文按顺序排列的示例。 如果有代理对,情况会更糟:“Music \ u {1d11e} for the win”。 - Florian Loitsch
@FlorianLoitsch 啊,谢谢你发现了这个问题。期待你的答案,这样我们就可以记录下正确反转所有字符串的方法了。我知道这是一个人为的任务,但它让我更多地了解了字符串。 - Seth Ladd

14

我为几个不同的替代方案做了一个小基准测试:

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));
}

这是结果:

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)

不是一个重大的改动,但在最后一个示例中使用递减循环而不是递增循环理论上应该使其更快。 - tbjgolden
最后一个有漏洞。但是基准测试提供了有用的信息。 - Dmitriy Blokhin
太好了!time() 方法?那是从某个包里面来的吗? - Gunnar Eketrapp

2

更多Dart包含一个轻量级的字符串包装器,使它们表现得像一个不可变的字符列表:

import 'package:more/iterable.dart';

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

2

试试这个函数

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));
}

(或)

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'));
}

这个程序出现问题的原因已经在排名较高的答案中描述了。 - Randal Schwartz

1

有一个工具包涵盖了这个功能。它还有一些更好的方法来操作字符串。

安装它:

dependencies:
  basic_utils: ^1.2.0

使用方法:

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

Github:

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


1

反转"Hello World"


虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。-【来自审查】 - Augustas
https://meta.stackoverflow.com/a/285557/7733418 - Yunnosch

0
这是一个可以用来反转字符串的函数。它以字符串作为输入,并使用名为Characters的Dart软件包从给定字符串中提取字符。然后我们可以将它们反转并重新连接以生成反转后的字符串。
String reverse(String string) {
  if (string.length < 2) {
    return string;
  }

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

0

创建此扩展:

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

使用方法:

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

0

** 这里有一个可以用来反转字符串的函数。它接受一个字符串作为输入,并使用名为split()的dart包从给定的字符串中提取字符。然后我们可以将它们反转并重新连接以生成反转后的字符串。 **

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接