Подтвердить что ты не робот

Неожиданное поведение подстроки в С#

Определение метода Substring() в классе .net System.String похоже на это

public string Substring(int startIndex)

Где startIndex - "Начальная позиция символа нулевой основы подстроки в этом экземпляре" в соответствии с определением метода. Если я правильно понимаю, это означает, что он даст мне часть строки, начиная с нулевого индекса.

Теперь, если у меня есть строка "ABC" и взять подстроку с разными индексами, я получаю следующие результаты.

var str = "ABC";
var chars = str.ToArray(); //returns 3 char 'A', 'B', 'C' as expected

var sub2 = str.Substring(2); //[1] returns "C" as expected
var sub3 = str.Substring(3); //[2] returns "" ...!!! Why no exception??
var sub4 = str.Substring(4); //[3] throws ArgumentOutOfRangeException as expected

Почему это не исключает исключение для случая [2]??

Строка имеет 3 символа, поэтому индексы [0, 1, 2], и даже ToArray(), ToCharArray() метод возвращает 3 символа, как ожидалось! Не следует ли это исключать, если я пытаюсь Substring() с начальным индексом 3?

4b9b3361

Ответ 1

Документация довольно четко говорит о правильном поведении:

Возвращаемое значение: строка, эквивалентная подстроке, начинающейся с startIndex в этом экземпляре, или Пустой, если startIndex равен длине этого экземпляра.

Выбрасывает ArgumentOutOfRangeException, если startIndex меньше нуля или * больше длины этого экземпляра. *

Другими словами, взятие подстроки, начинающейся чуть выше финального символа, даст вам пустую строку.

Ваш комментарий, что вы ожидали, что он даст вам часть строки, несовместим с этим. "Часть строки" также включает в себя множество всех подстрок нулевой длины, о чем свидетельствует тот факт, что s.substring(n, 0) также даст пустую строку.

Ответ 2

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

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

0   1   2   3
| A | B | C |   "ABC"

0   1   2   3   4   5   6   7   8   9
| M | y |   | S | t | r | i | n | g |   "My String"

В этой аналогии string.Substring(n) возвращает a string панелей, начинающихся с fencepost n. Обратите внимание, что последний символ строки имеет после него забор. Вызов функции с этим столбом забора возвращает значение, указывающее, что после этой точки нет панелей ограждений (т.е. Он возвращает пустой string).

Аналогично, string.Substring(n, l) возвращает string панелей l, начиная с fencepost n. Вот почему что-то вроде "ABC".Substring(2, 0) также возвращает "".

Ответ 3

Иногда просмотр кода может быть удобным:

Сначала это называется:

public string Substring(int startIndex)
{
    return this.Substring(startIndex, this.Length - startIndex);
}

Длина равна 0 из-за вычитания значения:

public string Substring(int startIndex, int length)
{
    if (startIndex < 0)
    {
        throw new ...
    }
    if (startIndex > this.Length)
    {
        throw new ...
    }
    if (length < 0)
    {
        throw new ...
    }
    if (startIndex > (this.Length - length))
    {
         throw new ...
    }
    if (length == 0) // <-- NOTICE HERE
    {
        return Empty;
    }
    if ((startIndex == 0) && (length == this.Length))
    {
        return this;
    }
    return this.InternalSubString(startIndex, length);
}

Ответ 4

Основываясь на том, что написано на MSDN:

*

Возвращаемое значение. Строка, эквивалентная подстроке, начинающейся с startIndex в этом экземпляре, или Пустое, если startIndex равно длине этого экземпляра.

Исключения ArgumentOutOfRangeException - startIndex меньше нуля или больше длины этого экземпляра

*

Ответ 5

Если посмотреть на String.Substring Method, пустая строка будет возвращена, если начальный индекс равен длине.

Строка, эквивалентная подстроке длины длины, которая начинается с startIndex в этом экземпляре или Empty, если startIndex равен к длине этого экземпляра, а длина равна нулю.

Ответ 6

Что такое Substring, так это то, что он проверяет, является ли startIndex больше длины строки, и только тогда она выдает исключение. В вашем случае он равен (длина строки равна 3). После этого он проверяет, равна ли длина подстроки нулевой, а если она возвращает String.Empty. В вашем случае длина подстроки равна длине строки (3) минус startIndex (3). Вот почему длина подстроки равна 0 и возвращается пустая строка.

Ответ 7

Все строки в С# в конце имеют String.Empty.

Вот хороший ответ по этому вопросу.

От MSDN - String Класс (система):

В .NET Framework объект String может включать внедренный null символов, которые считаются частью длины строки. Однако в некоторые языки, такие как C и С++, нулевой символ указывает конец строки; он не считается частью строки и не является считается частью длины строки.

Ответ 8

Чтобы дополнить другие ответы, Mono также правильно реализует это поведение.

public String Substring (int startIndex)
{
    if (startIndex == 0)
        return this;
    if (startIndex < 0 || startIndex > this.length)
        throw new ArgumentOutOfRangeException ("startIndex");

    return SubstringUnchecked (startIndex, this.length - startIndex);
}

// This method is used by StringBuilder.ToString() and is expected to
// always create a new string object (or return String.Empty). 
internal unsafe String SubstringUnchecked (int startIndex, int length)
{
    if (length == 0)
        return String.Empty;

    string tmp = InternalAllocateStr (length);
    fixed (char* dest = tmp, src = this) {
        CharCopy (dest, src + startIndex, length);
    }
    return tmp;
}

Как вы можете видеть, он возвращает String.Empty, если длина равна нулю.