ТЛ; др
У вас было неправильное представление о том, как String.Substring()
метод работает : вторым аргументом должна быть длина подстроки для извлечения, а не конец индекс (позиция символа) - см. ниже.
В качестве альтернативы вы можете использовать более краткую (хотя и более сложную) regex операцию с
-replace
для извлечения интересующая подстрока в одной операции - см. ниже.
В целом, лучше использовать HTML-парсер для извлечения желаемой информации, потому что обработка строк хрупкая (HTML допускает изменения в пустом пространстве , стиль цитирования, ...).
Как указывает Lee_Dailey , у вас было неправильное представление о том, как String.Substring()
метод работает : его аргументы:
- a начальный индекс (позиция символа на основе
0
),
- , из которого должна быть возвращена подстрока заданной длины .
Вместо этого вы попытались передать другой index в качестве аргумента length .
Чтобы исправить это, вы должны вычесть нижний индекс из старшего , чтобы получить длину подстроки, которую вы хотите извлечь:
Упрощенный пример:
# Sample input from which to extract the substring
# '>>this up to here'
# or, better,
# 'this up to here'.
$webpage = 'Return from >>this up to here<<'
# WRONG (your attempt):
# *index* of 2nd substring is mistakenly used as the *length* of the
# substring to extract, which in this even *breaks*, because a length
# that exceeds the bounds of the string is specified.
$webpage.Substring(
$webpage.IndexOf('>>'),
$webpage.IndexOf('<<')
)
# OK, extracts '>>this up to here'
# The difference between the two indices is the correct length
# of the substring to extract.
$webpage.Substring(
($firstIndex = $webpage.IndexOf('>>')),
$webpage.IndexOf('<<') - $firstIndex
)
# BETTER, extracts 'this up to here'
$startDelimiter = '>>'
$endDelimiter = '<<'
$webpage.Substring(
($firstIndex = $webpage.IndexOf($startDelimiter) + $startDelimiter.Length),
$webpage.IndexOf($endDelimiter) - $firstIndex
)
Общие замечания по поводу .Substring()
:
В следующих случаях этот метод .NET выдает исключение , которое PowerShell выдает как ошибку, определяющую оператор ; то есть по умолчанию оператор сам завершается, но выполнение продолжается :
Если вы укажете индекс, который находится за пределами строки (позиция символа на основе 0
меньше 0
или на единицу больше длины строки):
'abc'.Substring(4) # ERROR "startIndex cannot be larger than length of string"
Если вы укажете длину, конечная точка которой будет выходить за границы строки (если индекс плюс длина дает индекс, который больше длины строки).
'abc'.Substring(1, 3) # ERROR "Index and length must refer to a location within the string"
Тем не менее, вы можете использовать одно регулярное выражение ( регулярное выражение ) для извлечения интересующей подстроки через оператор -replace
$webpage = 'Return from >>this up to here<<'
# Outputs 'this up to here'
$webpage -replace '^.*?>>(.*?)<<.*', '$1'
Ключ заключается в том, чтобы регулярное выражение соответствовало всей строке и извлекало интересующую подстроку через группу захвата ((...)
), значение которой ($1
) может затем использоваться в качестве строки замены, эффективно возвращая только это.
Для получения дополнительной информации о -replace
см. этот ответ .
Примечание: В вашем конкретном случае требуется дополнительная настройка, потому что вы имеете дело с mutiline строкой:
$webpage -replace '(?s).*?<div class="entry-content">(.*?)Previous Chapter.*', '$1'
Встроенный параметр ((?...)
) s
гарантирует, что метасимвол .
также соответствует символ новой строки символов (так что .*
соответствует в строках ), что по умолчанию это не так.
Обратите внимание, что вам, возможно, придется применить экранирование к строкам поиска для встраивания в регулярное выражение, если они содержат регулярные выражения метасимволы (символы со специальным значением в контекст регулярного выражения):
Со встроенными литеральными строками, \
- экранировать символы по мере необходимости; например, бежать .txt
как \.txt
Если строка для вставки происходит из переменной , сначала примените [regex]::Escape()
к ее значению; e.g.:
$var = '.txt'
# [regex]::Escape() yields '\.txt', which ensures
# that '.txt' doesn't also match '_txt"
'a_txt a.txt' -replace ('a' + [regex]::Escape($var)), 'a.csv'