PowerShell 中的子字符串用于截断字符串长度

问题描述 投票:0回答:8

是否可以在 PowerShell 中截断字符串(使用

SubString()
?)到给定的最大字符数,甚至(如果原始字符串已经更短)?

例如:

foreach ($str in "hello", "good morning", "hi") { $str.subString(0, 4) }

截断适用于

hello
good morning
,但我收到
hi
的错误。

我想要以下结果:

hell
good
hi
string powershell substring
8个回答
29
投票

您需要评估当前项目并获取其长度。如果长度小于 4,则在子字符串函数中使用它。

foreach ($str in "hello", "good morning", "hi") {
    $str.subString(0, [System.Math]::Min(4, $str.Length)) 
}

15
投票

或者您可以使用 PowerShell 的三元运算符替代方案来保持简单:

foreach ($str in "hello", "good morning", "hi") {
  $(if ($str.length -gt 4) { $str.substring(0, 4) } else { $str })
}

虽然所有其他答案都是“正确的”,但它们的效率从次优变成了潜在的可怕。以下内容并不是对其他答案的批评,而是旨在对其基本操作进行指导性比较。毕竟,脚本编写更多的是让它尽快运行,而不是让它快速运行。

按顺序:

  1.  

    foreach ($str in "hello", "good morning", "hi") {
        $str.subString(0, [System.Math]::Min(4, $str.Length))
    }
    

    这与我的产品基本相同,只是我们调用 substring 并告诉它返回整个字符串,而不是在 $str 太短时返回它。因此,次优。它仍在执行 if..then..else,但就在 Min 内,vis.

    if (4 -lt $str.length) {4} else {$str.length}
    
  2.  

    foreach ($str in "hello", "good morning", "hi") { $str -replace '(.{4}).+','$1' }
    

    使用正则表达式匹配抓取前四个字符,然后用它们替换整个字符串意味着整个(可能很长)字符串必须由未知复杂度/效率的匹配引擎扫描。

    虽然人们可以看到“.+”只是匹配字符串的整个剩余部分,但匹配引擎可能会构建一个大的回溯替代列表,因为模式没有锚定(开头没有 ^)。这里巧妙的一点(未描述)是,如果字符串少于五个字符(四次

    .
    ,后跟一个或多个
    .
    ),则整个匹配失败,替换返回未更改的 $str。

  3.  

    foreach ($str in "hello", "good morning", "hi") {
      try {
        $str.subString(0, 4)
      }
      catch [ArgumentOutOfRangeException] {
        $str
      }
    }
    

    故意抛出异常而不是编程边界检查是一个有趣的解决方案,但谁知道当异常从 try 块冒泡到 catch 时会发生什么。在这个简单的情况下可能不多,但它不会是推荐的一般做法,除非存在许多可能的错误源(使得检查所有错误源变得很麻烦),但只有几个响应。

有趣的是,在其他地方使用

-join
和数组切片回答类似问题(这不会导致索引超出范围的错误,只是忽略丢失的元素):

$str[0..3] -join ""   # Infix

(或更简单)

-join $str[0..3]      # Prefix
鉴于

string

char[]
 的存储之间的高度相似性,
可能是最有效的(经过适当的优化)。需要优化,因为默认情况下, $str[0..3] 是一个 object[],每个元素都是一个字符,因此与字符串(在内存中)几乎没有相似之处。给 PowerShell 一点提示可能会有用,

-join [char[]]$str[0..3]

但是,也许只是告诉它你真正想要的,

new-object string (,$str[0..3]) # Need $str[0..3] to be a member of an array of constructor arguments

从而直接调用

new String(char[])

最好了。


1
投票

你可以捕获异常:

foreach ($str in "hello", "good morning", "hi") { 
  try { 
    $str.subString(0, 4) 
  }
  catch [ArgumentOutOfRangeException] {
    $str
  }
}

1
投票

更多正则表达式的爱,使用lookbehind:

PS > 'hello','good morning','hi' -replace '(?<=(.{4})).+'
hell
good
hi

1
投票

我一如既往地参加聚会迟到了!我已经使用 PadRight 字符串函数来解决这样的问题。与其他建议相比,我无法评论其相对效率:

foreach ($str in "hello", "good morning", "hi") { $str.PadRight(4, " ").SubString(0, 4) }

0
投票

您还可以使用

-replace

foreach ($str in "hello", "good morning", "hi") { $str -replace '(.{4}).+','$1' }

hell
good
hi

0
投票

旧线程,但我遇到了同样的问题并最终得到以下结果:-

$str.padright(4,"✓").substring(0,4).replace("✓","")

将 ✓ 字符替换为您想要的任何流氓角色。我使用了按键盘上的 ALT GR 和反引号键获得的字符。


0
投票

呃,我觉得很脏,但它是:

-join ("123123123".ToCharArray() | select -first 42)
输出完整字符串:
123123123

-join ("123123123".ToCharArray() | select -first 3)
输出前 3 个字符:
123

更简单,还是脏!

-join "123123123"[0..3]
(记住,它是从零开始的,所以有4个字符,相应调整)

© www.soinside.com 2019 - 2024. All rights reserved.