Regex 大小写基于通配符模式规范化文件名

Regex 大小写基于通配符模式规范化文件名,regex,powershell,case-sensitive,file-rename,wildcard-expansion,Regex,Powershell,Case Sensitive,File Rename,Wildcard Expansion,如何基于匹配通配符模式的文本组件对文件名进行大小写规范化 请考虑以下文件名: ABC_1232.txt abC_4321.Txt qwerty_1232.cSv QwErTY_4321.CsV 它们都匹配以下通配符模式: QWERTY_*.csv abc_*.TXT 注意模式的文字成分(例如,query,.csv)与上面列表中的匹配文件(例如,QwErTY,.csv)在大小写上的差异 我想重命名匹配的文件,以便在文件名中使用模式的文本部分;因此,产生的名称应为: abc_1232.TXT a

如何基于匹配通配符模式的文本组件对文件名进行大小写规范化

请考虑以下文件名:

ABC_1232.txt
abC_4321.Txt
qwerty_1232.cSv
QwErTY_4321.CsV
它们都匹配以下通配符模式:

QWERTY_*.csv
abc_*.TXT
注意模式的文字成分(例如,
query
.csv
)与上面列表中的匹配文件(例如,
QwErTY
.csv
)在大小写上的差异

我想重命名匹配的文件,以便在文件名中使用模式的文本部分;因此,产生的名称应为:

abc_1232.TXT
abc_4321.TXT
QWERTY_1232.csv
QWERTY_4321.csv

这是一个启发性的问题。

术语说明:由于模式可以用来指通配符表达式和正则表达式,因此该术语被用作通配符表达式的明确缩写


基于字符串拆分的简单但有限的解决方案 具体来说,下面的解决方案仅限于包含一个
*
作为唯一通配符元字符的通配符模式

# Sample input objects that emulate file-info objects
# as output by Get-ChildItem
$files =
    @{ Name = 'ABC_1232.txt' },
    @{ Name = 'abC_4321.TxT' },
    @{ Name = 'qwerty_1232.cSv' },
    @{ Name = 'QwErTY_4321.CsV' },
    @{ Name = 'Unrelated.CsV' }

# The wildcard patterns to match against.
$globs = 'QWERTY_*.csv', 'abc_*.TXT'

# Loop over all files.
# IRL, use Get-ChildItem in lieu of $files.
$files | ForEach-Object {    
  # Loop over all wildcard patterns
  foreach ($glob in $globs) {    
    if ($_.Name -like $glob) { # matching filename    
      # Split the glob into the prefix (the part before '*') and
      # the extension (suffix), (the part after '*').
      $prefix, $extension = $glob -split '\*'

      # Extract the specific middle part of the filename; the part that 
      # matched '*'
      $middle = $_.Name.Substring($prefix.Length, $_.Name.Length - $prefix.Length - $extension.Length)

      # This is where your Rename-Item call would go.
      #   $_ | Rename-Item -WhatIf -NewName ($prefix + $middle + $extension)
      # Note that if the filename already happens to be case-exact, 
      # Rename-Item is a quiet no-op.
      # For this demo, we simply output the new name.
      $prefix + $middle + $extension    
    }
  }
}

具有正则表达式的广义但更复杂的解决方案 此解决方案要复杂得多,但应适用于所有通配符表达式(只要
`
-不需要支持转义)

#模拟文件信息对象的示例输入对象
#作为Get ChildItem的输出
$files=
@{Name='ABC_1232.txt'},
@{Name='abC_4321.TxT'},
@{Name='qwerty_1232.cSv'},
@{Name='QwErTY_4321.CsV'},
@{Name='Unrelated.CsV'}
#要匹配的全局(通配符模式)。
$globs='QWERTY.*.csv','abc.*.TXT'
#将glob转换为正则表达式,非文本部分包含在
#捕获组;注意添加了锚^和$,因为globs
#匹配整个输入字符串。
#例如,“QWERTY.*.csv”->“^QWERTY(.*)\.csv$”
$regexes=foreach($glob中的glob){
'^' +
([regex]::Escape($glob)-替换“\\\*”,“(.*)”-替换*
“\\?”,“()”-替换“\?”?
'\\(\[.+?\])', '($1)') + # [...]
'$'
}
#从可以与-f一起使用的globs构造字符串模板
#运算符填写每个文件名匹配的变量部分。
#每个变量部分都替换为{}占位符,从0开始。
#例如,“QWERTY*.csv”->“QWERTY{0}.csv”
$templates=foreach($glob中的glob){
$iRef=[ref]0
[regex]::替换(
($glob-replace'[{}]','$&$&'),#首先将转义文字'{'和'}'替换为'{{'和'}}'
“\*\?\\[.+?\]”,通配符元字符。/constructs
{param($match){'+($iRef.Value++++'}}}}替换为{}占位符
)
}
#循环所有文件。
#IRL,使用Get ChildItem代替$files。
$files | ForEach对象{
#循环所有通配符模式
$i=-1
foreach($regex中的regex){
++$i
#查看文件名是否匹配
if($matchInfo=[regex]::Match($\u.Name,$regex,'IgnoreCase')).Success){
#使用捕获组值实例化模板字符串。
#例如,“QWERTY_{0}.csv'-f'4321”
$newName=$templates[$i]-f($matchInfo.Groups.Value |选择对象-跳过1)
#这就是您的重命名项调用的目的地。
#$124;重命名项-WhatIf-NewName$NewName
#请注意,如果文件名恰好是大小写精确的,
#重命名项是一个安静的禁止操作。
#对于这个演示,我们只需输出新名称。
$newName
}
}
}

绝对精彩-这是另一个@mklement0添加到收藏夹中@伊奇登:)我很高兴听到答案是有用的,谢谢你的反馈。
# Sample input objects that emulate file-info objects
# as output by Get-ChildItem
$files =
    @{ Name = 'ABC_1232.txt' },
    @{ Name = 'abC_4321.TxT' },
    @{ Name = 'qwerty_1232.cSv' },
    @{ Name = 'QwErTY_4321.CsV' },
    @{ Name = 'Unrelated.CsV' }

# The globs (wildcard patterns) to match against.
$globs = 'QWERTY_*.csv', 'abc_*.TXT'

# Translate the globs into regexes, with the non-literal parts enclosed in
# capture groups; note the addition of anchors ^ and $, given that globs
# match the entire input string.
# E.g., 'QWERTY_*.csv' -> '^QWERTY_(.*)\.csv$'
$regexes = foreach($glob in $globs) {
  '^' +
    ([regex]::Escape($glob) -replace '\\\*', '(.*)' -replace  # *
                                     '\\\?', '(.)' -replace   # ?
                                     '\\(\[.+?\])', '($1)') + # [...]
  '$'
}

# Construct string templates from the globs that can be used with the -f
# operator to fill in the variable parts from each filename match.
# Each variable part is replaced with a {<n>} placeholder, starting with 0.
# E.g., 'QWERTY_*.csv' -> 'QWERTY_{0}.csv'
$templates = foreach($glob in $globs) {
  $iRef = [ref] 0
  [regex]::Replace(
    ($glob -replace '[{}]', '$&$&'), # escape literal '{' and '}' as '{{' and '}}' first
    '\*|\?|\[.+?\]', # wildcard metachars. / constructs
    { param($match) '{' + ($iRef.Value++) + '}' } # replace with {<n>} placeholders
  )
}

# Loop over all files.
# IRL, use Get-ChildItem in lieu of $files.
$files | ForEach-Object {

  # Loop over all wildcard patterns
  $i = -1
  foreach ($regex in $regexes) {
    ++$i
    # See if the filename matches
    if (($matchInfo = [regex]::Match($_.Name, $regex, 'IgnoreCase')).Success) {
      # Instantiate the template string with the capture-group values.
      # E.g., 'QWERTY_{0}.csv' -f '4321' 
      $newName = $templates[$i] -f ($matchInfo.Groups.Value | Select-Object -Skip 1)

      # This is where your Rename-Item call would go.
      #   $_ | Rename-Item -WhatIf -NewName $newName
      # Note that if the filename already happens to be case-exact, 
      # Rename-Item is a quiet no-op.
      # For this demo, we simply output the new name.
      $newName    
    }
  }
}