Как изменить строку в Dart?

У меня есть String, и я хотел бы отменить его. Например, я пишу фильтр AngularDart, который меняет строку. Это просто для демонстрационных целей, но это заставило меня задуматься о том, как изменить направление строки.

Пример:

Hello, world

должен превратиться в:

dlrow ,olleH

Я также должен рассматривать строки с символами Unicode. Например: 'Ame\u{301}lie'

Какой простой способ изменить строку, даже если она есть?

Ответ 1

Вопрос не определен. Реверсирование произвольных строк не имеет смысла и приведет к нарушению вывода. Первым (преодолимым) препятствием является Утф-16. Строки Дарта кодируются как 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, а не на границах руны [.]

Существует простое решение для этого: вместо того, чтобы изменять отдельные единицы кода, можно отменить руны:

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

Но это не все. Руны тоже могут иметь определенный порядок. Это второе препятствие гораздо труднее решить. Простой пример:

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

Обратите внимание, что акцент находится на неправильном символе.

Возможно, существуют и другие языки, которые еще более чувствительны к порядку отдельных рун.

Если ввод имеет серьезные ограничения (например, Ascii, или Iso Latin 1), то реверсивные строки технически возможны. Тем не менее, я еще не видел ни одного случая использования, когда эта операция имела смысл.

Использование этого вопроса в качестве примера для показа того, что строки имеют подобные List-операции, также не является хорошей идеей. За исключением нескольких случаев использования, строки должны обрабатываться по отношению к определенному языку и с очень сложными методами, которые имеют знание, специфичное для языка.

В частности, говорящие на родном английском языке должны обратить внимание: строки редко можно обрабатывать, как если бы они были списками отдельных символов. Практически на любом другом языке это приведет к ошибкам программ. (И не заставляйте меня начинать с toLowerCase и toUpperCase...).

Ответ 2

Здесь один из способов отменить строчку ASCII в дротике:

input.split('').reversed.join('');
  • разделите строку на каждый символ, создав список
  • сгенерировать итератор, который меняет список
  • присоединиться к списку (создание новой строки)

Примечание: это не обязательно самый быстрый способ изменить строку. См. Другие ответы на альтернативы.

Примечание. Это неправильно обрабатывает все строки unicode.

Ответ 3

Я сделал небольшой ориентир для нескольких альтернатив:

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)

Ответ 4

Попробуйте эту функцию

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

Ответ 5

Библиотека Подробнее Dart содержит легкую обертку вокруг строк, которая заставляет их вести себя как неизменный список символов:

import 'package:more/iterable.dart';

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