如何通过PowerShell正确读取MP3文件ID3v2标签中的ISRC信息
PowerShell 提取MP3 ISRC信息落地方案
方案1:原生系统接口读取(推荐,无解析误差)
ID3v2标准中ISRC信息存储在TSRC帧内,不需要做任何字符串匹配,直接调用Windows自带的Shell元数据接口即可读取,和第三方标签工具读取结果一致。
- 先遍历属性索引,匹配ISRC对应的属性位(不同Windows版本索引值存在差异):
$shell = New-Object -ComObject Shell.Application # 替换为你的音乐库根目录 $testFolder = $shell.Namespace('D:\Music') 0..400 | ForEach-Object { $propName = $testFolder.GetDetailsOf($testFolder.Items(), $_) if ($propName -match 'ISRC') { Write-Host "ISRC属性索引: $_ ,属性名: $propName" } } - 拿到索引后(Win10/11默认索引为292,以实际遍历结果为准),批量遍历所有MP3生成路径-ISRC哈希表:
$isrcMap = @{} $allMp3 = Get-ChildItem -Path 'D:\Music' -Filter *.mp3 -Recurse $shell = New-Object -ComObject Shell.Application foreach ($mp3 in $allMp3) { $currentFolder = $shell.Namespace($mp3.DirectoryName) $fileItem = $currentFolder.ParseName($mp3.Name) # 将292替换为你上一步查到的索引值 $isrcValue = $currentFolder.GetDetailsOf($fileItem, 292).Trim() if ($isrcValue) { $isrcMap[$mp3.FullName] = $isrcValue } } # 输出验证结果 $isrcMap
方案2:二进制解析ID3标签(适配接口读取失败场景)
之前用Get-Content直接读文本提取失败的核心原因:ID3v2文本帧开头的ÿþ是UTF-16LE编码的BOM标记(对应字节0xFF 0xFE),这类编码下每个英文字符占2字节,高字节为0x00,在PowerShell默认文本读取逻辑下会被识别为不可见空字符,占用字符串长度但不显示,直接做字符串偏移截取必然错位。
以下代码严格遵循ID3v2帧结构解析,自动适配编码,不需要手动匹配文本位置:
$isrcMap = @{} $allMp3 = Get-ChildItem -Path 'D:\Music' -Filter *.mp3 -Recurse foreach ($mp3 in $allMp3) { $fileStream = [System.IO.File]::OpenRead($mp3.FullName) $binaryReader = New-Object System.IO.BinaryReader($fileStream) # 校验ID3v2头 $id3Header = [System.Text.Encoding]::ASCII.GetString($binaryReader.ReadBytes(3)) if ($id3Header -ne 'ID3') { $binaryReader.Close() $fileStream.Close() continue } # 读取标签总长度 $binaryReader.ReadBytes(2) | Out-Null $binaryReader.ReadByte() | Out-Null $sizeBytes = $binaryReader.ReadBytes(4) $tagTotalSize = ($sizeBytes[0] -shl 21) -bor ($sizeBytes[1] -shl 14) -bor ($sizeBytes[2] -shl 7) -bor $sizeBytes[3] $tagContent = $binaryReader.ReadBytes($tagTotalSize) $binaryReader.Close() $fileStream.Close() # 遍历帧定位TSRC $offset = 0 $targetIsrc = $null while ($offset -lt $tagContent.Length - 10) { $frameId = [System.Text.Encoding]::ASCII.GetString($tagContent[$offset..($offset+3)]) $frameSize = [BitConverter]::ToInt32($tagContent[($offset+7)..($offset+4)], 0) if ($frameId -eq 'TSRC') { $encodingFlag = $tagContent[$offset+10] $frameData = $tagContent[($offset+11)..($offset+10+$frameSize-1)] $contentEncoding = switch ($encodingFlag) { 0 { [System.Text.Encoding]::GetEncoding('ISO-8859-1') } 1 { [System.Text.Encoding]::Unicode } 3 { [System.Text.Encoding]::UTF8 } default { [System.Text.Encoding]::UTF8 } } $targetIsrc = $contentEncoding.GetString($frameData).Trim([char]0x00, [char]0xFEFF) break } $offset += 10 + $frameSize } if ($targetIsrc) { $isrcMap[$mp3.FullName] = $targetIsrc } } $isrcMap
注意:不要使用
Get-Content读取二进制MP3文件后做固定偏移字符串匹配,一旦标签内存在嵌入封面、自定义字段长度变化,匹配逻辑会完全失效。
内容的提问来源于stack exchange,提问作者AlexRussell
相关产品推荐
相关产品推荐

