как метод String.Split определяет приоритет разделителя при передаче нескольких многосимвольных разделителей?

Если у вас есть этот код:

"......".Split(new String[]{"...", ".."}, StringSplitOptions.None);

Результирующие элементы массива:

 1. ""
 2. ""
 3. ""

Теперь, если вы измените порядок разделителей,

"......".Split(new String[]{"..", "..."}, StringSplitOptions.None);

Результирующие элементы массива:

 1. ""
 2. ""
 3. ""
 4. ""

Из этих двух примеров я склонен заключить, что метод Split рекурсивно токенизирует по мере прохождения каждого элемента массива слева направо.

Однако, как только мы добавим в уравнение разделители, содержащие буквенно-цифровые символы, станет ясно, что приведенная выше теория неверна.

  "5.x.7".Split(new String[]{".x", "x."}, StringSplitOptions.None)

результат: 1. "5" 2. ".7"

   "5.x.7".Split(new String[]{"x.", ".x"}, StringSplitOptions.None)

результат: 1. "5" 2. ".7"

На этот раз мы получаем тот же результат, что означает, что правило, теоретизированное на основе первого набора примеров, больше не применяется. (то есть: если бы приоритет разделителя всегда определялся на основе положения разделителя в массиве, то в последнем примере мы бы получили "5." и "7" вместо "5" и ".7".

Что касается того, почему я трачу свое время, пытаясь угадать, как работает стандартный API .NET, это потому, что я хочу реализовать аналогичную функциональность для своих java-приложений, но ни StringTokenizer, ни org.apache.commons.lang.StringUtils не предоставляют возможность разделить Строка с использованием нескольких многосимвольных разделителей (и даже если бы я нашел API, предоставляющий эту возможность, было бы трудно понять, всегда ли он выполняет токенизацию с использованием того же алгоритма, что и String.Split метод.


person John Smith    schedule 07.02.2013    source источник
comment
String#split в Java принимает регулярное выражение в качестве критерия разделения. Таким образом, вы можете объединить столько критериев разделения, используя pipe(|). Кроме того, было бы лучше, если бы вы могли опубликовать реальную проблему здесь, а не эквивалентный код на другом языке. Не все люди знают несколько языков.   -  person Rohit Jain    schedule 08.02.2013
comment
@RohitJain: Тем не менее, мне было бы интересно узнать, что такое алгоритм .NET.   -  person Matthew    schedule 08.02.2013


Ответы (3)


Из MSDN:

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

Так, для первого случая ".." и "..." находятся на одной позиции и их порядок в разделителе используется для определения используемого. Во втором случае «.x» находится перед «x». и порядок элементов в разделителе не применяется.

person J. Calleja    schedule 07.02.2013
comment
+1 Я как раз собирался опубликовать это. Нет ничего лучше, чем перейти непосредственно к документации... - person Yuck; 08.02.2013
comment
Меня удивляет, что документы такие строгие. Не ожидал этого, поскольку они иногда довольно поверхностны. - person usr; 08.02.2013

Я быстро взглянул на это... и оказалось, что закрытый метод MakeSeparatorList в классе string на самом деле извлекает массив индексов... но он будет соответствовать первому найденному.

Итак, поскольку .x стоит перед x. в обоих ваших примерах, этот индекс сохраняется.

Это код, который я использовал для тестирования:

var s = "5.x.7";

string[] separators = new string[] { "x.", ".x" };
int[] sepList = new int[1024];
int[] lengthList = new int[1024];

MethodInfo dynMethod = s.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Last(x => x.Name == "MakeSeparatorList");
dynMethod.Invoke(s, new object[] { separators, sepList, lengthList });

Debugger.Break();

Смотрите этот снимок экрана:

(Мой скриншот не отображается? :/)

Обратите внимание, что индекс равен 1 (что приводит к .x), несмотря на то, что .x является второй записью в массиве.

person Simon Whitehead    schedule 07.02.2013
comment
Могу я просто сказать... вау, этот код в классе строк ужасен. num1, num4, указатели с ужасными именами.. это так плохо. - person Simon Whitehead; 08.02.2013
comment
Локальные имена не сохраняются в IL. Иногда Reflector может извлечь имена из PDB, я думаю, что не всегда. Он должен составить эти имена. - person usr; 08.02.2013
comment
Он неплохо справляется с большинством сборок... но особенно плохи библиотеки BCL. - person Simon Whitehead; 08.02.2013

строка .split разделяет первый совпадающий символ, соответствующий аргументу. В простом вопросе: скажем, вы предоставили опцию split («a», «b»), а строка содержит «appaleisbigapll», алгоритм прост, он начинается с первого символа и соответствует любому из a или b. если он находит их, он разделяется и начинается со следующего символа. в вашем примере

5.x.7 с «.x», «x.». Он управляет с помощью оператора «или», поэтому сначала находит .x, а теперь проверяет оставшиеся .7, так как не осталось совпадающего символа, поэтому он оставляет .7 как есть. Результат 5 и .7

То же самое происходит во втором вопросе, он находит .x и, как правило, говорит .x или x. продолжить с .7 приоритет здесь не применяется. И для вашего первого набора примеров да, он рекурсивно выполняет операцию разделения.

person Praveen Kumar Patidar    schedule 07.02.2013
comment
Да, он рекурсивно выполняет операцию разделения, я бы сказал, что это неправильно. Это выглядит так только в первом примере, потому что они расположены в том порядке, в котором он соответствует. Такого на самом деле не бывает. - person Simon Whitehead; 08.02.2013