2015-10-02 2 views
26

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

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?

+0

Может быть символом '\ 0' (чтобы отметить конец строки). Но я не уверен, использует ли это .NET. Стоит google хотя – Stefan

+11

строка 1246 @ http://referencesource.microsoft.com/#mscorlib/system/string.cs,1246 –

+1

Спасибо @AlexK. и другие (ответы) для указания документации по внедрению и MSDN. Я вижу, как это сделала команда фреймворков, но для меня (и немногих других, я думаю) это нечто неожиданное! –

ответ

1

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

Here is good answer по этому вопросу.

Из MSDN - String класса (система):

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

+6

Это просто неправильно сказать, что «все строки в конце имеют» «». Это как сказать, что все массивы в конце имеют еще один элемент, который является массивом (!) И не содержит элементов (!). Связанный ответ использует слова ** совпадения **, а не ** **. Что «подстрока» делает, когда запрашивается строка длины 0, - пустая строка возвращается, но это не потому, что она находится в конце строки или что-то вроде этого. – Sinatr

+0

@Sinatr мы это знаем только после того, как мы декомпилируем библиотеку –

+3

Нет, мы точно знаем, какая строка не имеет '' '' в конце. В 'C#' это истинно: '' некоторая строка "==" некоторая строка "+" "', но это не потому, что '' '' добавляется (и игнорируется во время сравнения) или существует в конце. Это потому, что ** ничего не происходит **, когда вы работаете с '' ''. 'Строка.Empty' является специальным случаем и будет возвращаться строковыми методами работы, когда 0 длина строки является результатом операции. – Sinatr

51

documentation довольно четко об этом будучи правильное поведение:

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

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

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

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

+0

Я вижу, что это «реализованное» поведение, но разве это не неожиданно и не смущает? –

+7

@ArghyaC, только некоторым, по-видимому :-) См. Мой последний абзац. Поскольку подстрока может включать объекты с нулевой шириной * между * символами (если вы запрашиваете длину нуля), имеет смысл, что вы также можете получить объект нулевой ширины после окончательного символа. – paxdiablo

+1

С этим последним абзацем это имеет смысл (в некотором роде). Затем он переходит в 'null' vs' string.Empty'. Но спасибо за объяснение :) –

4

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

*

Возвращаемое значение - Строка, которая эквивалентна подстроки, которая начинается в STARTINDEX в данном случае, или Empty, если STARTINDEX равен длина этого экземпляра.

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

*

4

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

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

2

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

12

Sometimes looking at the code can be handy:

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

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

Этот код показывает, что это довольно «решительное» поведение. Для 'Substring (n, 0)' return 'string.Empty' почти очевидно, но для' Substring (lastIndex + 1) '? Не так много, ИМХО. Но тогда это будут довольно упрямые дебаты :) –

+1

Согласен. Довольно странно –

+0

's.Substring (n)' возвращает 's.Substring (n, s.Length - n)'. Итак, 's.Substring (lastIndex + 1)' означает точно '(s.Substring (lastIndex + 1, 0)' ... –

22

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

Рассмотрим 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) возвращает string панелей, начиная с fencepost n , Обратите внимание, что последний символ строки имеет после него забор. Вызов функции с этим столбом забора возвращает значение, указывающее, что после этой точки нет панелей забора (т. Е. Он возвращает пустой string).

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

+0

+1 Я думаю, что многие концепции, связанные с указателями и индексами, лучше всего работают, если рассматривать указатели и индексы как идентификацию пробелов между элементами, а не сами идентифицировать элементы. Первый элемент находится между индексом 0 и индексом 1; второй находится между 1 и 2 и т. д. В случае строк иногда полезно рассматривать строки, за которыми следует бесконечное количество заградительных столбов, между которыми нет ничего (так, во многих версиях BASIC, например, «mid (" Hello ", 23,1)' отлично с удовольствием вернет пустую строку). Я бы хотел, чтобы авторы языка/рамки обычно включали в себя ... – supercat

+0

... методы, которые ловутся, когда не хватает fenceposts, а также методы, которые с радостью возвращают более короткие или пустые строки. Иногда код хочет сказать «Мне нужно ровно 5 символов, начиная с индекса 9», но иногда нужно «Мне нужно до 5 символов, начиная с индекса 9, если строка продолжается так далеко». Обе операции требуются достаточно часто, ИМХО, что стоит иметь отдельные методы для обоих. – supercat

+0

Это хорошая аналогия. Но, как правило, 'index' ведет себя как указатель на одно местоположение/элемент памяти в массиве, а не на местоположение между элементами. Не так ли? Почему он вел себя так, только для 'Подстроки'? Если это было общее поведение, то «ABC» .ToArray() [3] 'не должен бросать« IndexOutOfRangeException »IMHO. –

1

В дополнение к другим ответам 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, если длина равна нулю.

+1

Это хорошая компактная реализация в «Моно». И да, это функционально похоже на реализации FCL. –

Смежные вопросы