【问题标题】:String.IndexOf() returns unexpected value - cannot extract substring between two search stringsString.IndexOf() 返回意外值 - 无法在两个搜索字符串之间提取子字符串
【发布时间】:2019-03-14 15:07:18
【问题描述】:

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

我通过

获取网页内容
$webpage = (Invoke-WebRequest -URI 'https://wanderinginn.com/2018/03/20/4-20-e/').Content

这个 $webpage 应该是字符串类型。

现在

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

返回正确的值,但

$webpage.IndexOf("Previous Chapter")

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

理论上它应该剪切页面的“正文”,通过我要替换的专有名词列表运行它并将其推送到 htm 文件中。 一切正常,但 IndexOf("Prev...") 的值不起作用。

编辑: 在调用 webrequest 之后我可以

Set-Clipboard $webrequest

并在记事本++中发布此内容,在那里我可以找到'div class="entry-content"'和'Previous Chapter'。 如果我做类似的事情

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

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

【问题讨论】:

  • 你得到了什么意想不到的价值?您期望的价值是什么?为什么?
  • 这对我很有效(Invoke-WebRequest -URI 'https://wanderinginn.com/2018/03/20/4-20-e/').Content.indexof('Previous Chapter') 得到了我 87859。这有什么问题?您希望行号与字符号一样吗?
  • IndexOf() 只返回所请求字符串的整数索引。您需要使用该信息来切出您需要的内容。
  • .SubString() 方法单独使用 StartIndexStartIndex, Length`。你给它两个起始索引号。 ///// 您需要将第二个数字设置为两个索引值之间的差异。 [咧嘴]
  • 天哪,我是个笨蛋。谢谢!我想我让自己更难了,因为 notepad++ 的 find 总是显示与 powershell 不同的字符数。

标签: html powershell substring string-parsing


【解决方案1】:

tl;dr

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

  • 作为替代方法,您可以使用更简洁(尽管更复杂)regex 操作和
    -replace
    来提取单个操作中感兴趣的子字符串 - 见下文。

  • 总体而言,最好使用 HTML 解析器 来提取所需的信息,因为字符串处理脆弱(HTML 允许空格的变化, 引用风格, ...)。


正如Lee_Dailey 指出的那样,您对String.Substring() method 的工作原理有一个误解:它的论点是:

  • 起始索引(基于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 方法会引发 exception,PowerShell 会将其显示为 statement 终止错误;也就是说,默认情况下语句本身被终止,但执行继续

  • 如果您指定的索引超出了字符串的范围(基于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)然后可以用作替换字符串,有效地返回它。

有关-replace的更多信息,请参阅this answer

注意:在您的特定情况下需要进行额外的调整,因为您正在处理 multiline 字符串:

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

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

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

    • 如果要嵌入的字符串来自变量,请先将[regex]::Escape() 应用于其值;例如:

          $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'
      

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 2014-05-28
    • 2013-01-31
    • 2013-12-11
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    相关资源
    最近更新 更多