Подстрока в Powershell для усечения длины строки

можно ли в Powershell усечь строку (используя SubString()?), заданному максимальному количеству символов,даже если исходная строка уже короче ?

например :

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

усечение работает для hello и good morning но я получаю ошибку для hi.

Я хотел бы получить следующий результат:

hell
good
hi

4 ответов


вам нужно оценить текущий элемент и получить его длину. Если длина меньше 4, Используйте это в функции подстроки.

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

или вы можете просто сохранить его простым, используя альтернативу powershell тернарному оператору:

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

в то время как все остальные ответы "правильны", их эффективность переходит от неоптимальной к потенциально ужасающей. Следующее не является критикой других ответов, но предназначено как поучительное сравнение их основной операции. В конце концов, сценарии-это больше о том, чтобы запустить его в ближайшее время, чем быстро.

In порядок:

1.

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

это в основном то же самое, что и мое предложение, за исключением того, что вместо того, чтобы просто возвращать $str, когда он слишком короткий, мы вызываем подстроку и говорим ей вернуть всю строку. Следовательно, неоптимально. Он все еще делает if..затем..но только внутри мин, ВИС.

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

2.

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

использование сопоставления регулярных выражений для захвата первых 4 символов, а затем замены всей строки на них означает, что весь (возможно, очень длинная) строка должна сканироваться соответствующим механизмом неизвестной сложности / эффективности. В то время как человек может видеть, что '.+ 'просто соответствует всей остальной части строки, соответствующий движок может создавать большой список альтернатив обратного отслеживания, поскольку шаблон не закреплен (нет ^ в начале). (Не описанный) умный бит здесь заключается в том, что если строка меньше 5 символов (4 раза . затем 1 или более .) затем весь матч терпит неудачу и заменяет возвращает $str без изменений.

3.

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

преднамеренное выбрасывание исключений вместо программной проверки границ-интересное решение, но кто знает, что происходит, когда исключение пузырится от блока try до catch. Вероятно, не так много в этом простом случае, но это не было бы рекомендуемой общей практикой, за исключением ситуаций, когда есть много возможных источников ошибок (что делает его громоздким для проверки всех из них), но только несколько ответы.

интересно, что ответ на аналогичный вопрос в другом месте с использованием-join и array slices (которые не вызывают ошибок в индексе вне диапазона, просто игнорируют отсутствующие элементы)

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

(или проще)

-join $str[0..3]      # prefix

может быть наиболее эффективным (с соответствующей оптимизацией), учитывая сильное сходство между хранилищем string и char[]. Оптимизация потребуется, так как по умолчанию $str[0..3] является объектом, [], каждый элемент будучи одним символом, и поэтому имеет мало сходства со строкой (в памяти). Предоставление 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[])

лучше.


вы можете поймать исключение:

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

вы также можете использовать -replace

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

hell
good
hi