String.IndexOf()返回意外值-无法在两个搜索字符串之间提取子字符串

时间:2019-03-14 15:07:18

标签: html powershell substring string-parsing

用于处理网络故事中某些专有名称的脚本,以帮助我的阅读工具正确发音。

我通过以下方式获取网页的内容

$webpage = (Invoke-WebRequest -URI 'https://wanderinginn.com/2018/03/20/4-20-e/').Content

此$ web页面应为String类型。

现在

$webpage.IndexOf('<div class="entry-content">')

返回正确的值,

$webpage.IndexOf("Previous Chapter")

返回意外值,我需要一些解释为什么或如何自己找到错误。

从理论上讲,它应该剪切页面的“正文”,以通过我要替换的专有名词的列表运行它,并将其推入htm文件中。 都可以,但是IndexOf(“ Prev ...”)的值无效。

编辑: 在invoke-webrequest之后,我可以

Set-Clipboard $webrequest

并将其发布在notepad ++中,在那里我可以找到'div class =“ entry-content”'和'Previous Chapter'。 如果我做类似

Set-Clipboard $webpage.substring(
     $webpage.IndexOf('<div class="entry-content">'),
     $webpage.IndexOf('PreviousChapter')
   )

我希望Powershell能够正确确定这两个字符串的第一个实例并在它们之间进行剪切。因此,剪贴板现在应该具有我想要的内容,但是字符串比第一个出现的位置更远。

1 个答案:

答案 0 :(得分:1)

tl; dr

  • 您对String.Substring() method的工作方式有一个误解:第二个参数必须是要提取的子字符串的 length ,而不是结尾< em> index (字符位置)-参见下文。

  • 作为替代,您可以对
    -replace使用更简洁(尽管更复杂)的 regex 操作以一次操作提取感兴趣的子字符串-见下文。

  • 总体而言,最好使用 HTML解析器 提取所需的信息,因为字符串处理很容易 (HTML允许空白,引号样式,...)。


正如Lee_Dailey所指出的,您对String.Substring() method的工作方式有一个误解:其参数是:

  • 起始索引 (基于0的字符位置)
  • 应该从中返回给定 length 的子字符串。

相反,您尝试传递另一个 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"
    

也就是说,您可以使用单个 regex regular expression)通过-replace operator提取感兴趣的子字符串

$webpage = 'Return from >>this up to here<<'

# Outputs 'this up to here'
$webpage -replace '^.*?>>(.*?)<<.*', '$1'

关键是让正则表达式匹配整个字符串,并通过捕获组(...))提取感兴趣的子字符串,其值({{ 1}})可以用作替换字符串,有效地返回该字符串。

有关$1的更多信息,请参见this answer

注意:在您的特定情况下,还需要进行其他调整,因为您要处理 mutiline 字符串:

-replace
  • 内联选项($webpage -replace '(?s).*?<div class="entry-content">(.*?)Previous Chapter.*', '$1' (?...)确保元字符s也匹配 newline 字符(因此.匹配跨行),默认情况下不会。

  • 请注意,如果搜索字符串恰巧包含正则表达式元字符(具有特殊含义的字符),则可能必须对搜索字符串应用转义才能嵌入到正则表达式中在正则表达式中):

    • 使用嵌入的文字字符串,根据需要.*-转义字符;例如,将\转换为.txt

    • 如果要嵌入的字符串来自变量,请首先将\.txt应用于其值;例如:

      [regex]::Escape()