我正在尝试整理一份 Excel 表,列出我已向 Steam 添加快捷方式的所有游戏。Steam 允许您向非 Steam 游戏添加快捷方式并充当它们的启动器。用户添加的快捷方式链接位于名为 Shortcuts.vdf 的本地数据库中,其中包含与 Steam 拥有的游戏相关的所有信息。我基本上感兴趣的是游戏的名称。
以下是文件中的数据在记事本中的片段:
shortcuts 0 appid °rõÄappname Cemu Exe "T:\Cemu\App+Update+DLC\Cemu.exe" StartDir "T:\Cemu\App+Update+DLC\" icon ShortcutPath LaunchOptions IsHidden AllowDesktopConfig AllowOverlay openvr Devkit DevkitGameID LastPlayTime ZùÏ_ tags 0 favorite 1 All Games 1 appid }yâappname Citra Nightly Exe "C:\Users\phili\AppData\Local\Citra\nightly-mingw\citra-qt.exe" StartDir "C:\Users\phili\AppData\Local\Citra\nightly-mingw\" icon ShortcutPath LaunchOptions IsHidden AllowDesktopConfig AllowOverlay openvr Devkit DevkitGameID LastPlayTime ¿
ã_ tags 0 favorite 1 All Games 2 appid {Sü¸appname RollerCoaster Tycoon 2: Triple Thrill Pack Open Exe "C:\Program Files\OpenRCT2\openrct2.exe" StartDir "C:\Program Files\OpenRCT2\" icon ShortcutPath LaunchOptions IsHidden AllowDesktopConfig AllowOverlay openvr Devkit DevkitGameID LastPlayTime !Ý_ tags 0 steam 1 All Games 3 appid Iì»Âappname Art Of Fighting 2 Exe "R:\Amazon Games\Library\Art of Fighting 2\ArtOfFighting2.exe" StartDir "R:\Amazon Games\Library\Art of Fighting 2\" icon ShortcutPath LaunchOptions IsHidden AllowDesktopConfig AllowOverlay openvr Devkit DevkitGameID LastPlayTime tags 0 Shortcuts 1 Amazon 2 All Games 4 appid ñappname Origin Exe "C:\Program Files (x86)\Origin\Origin.exe" StartDir "C:\Program Files (x86)\Origin\" icon ShortcutPath LaunchOptions IsHidden AllowDesktopConfig AllowOverlay openvr Devkit DevkitGameID LastPlayTime ÛÊ*] tags 0 steam 1 All Games 5 appid £á@¤appname A Good Snowman Is Hard To Build Exe "R:\Amazon Games\Library\A Good Snowman Is Hard To Build\Snowman.exe" StartDir "R:\Amazon Games\Library\A Good Snowman Is Hard To Build\" icon ShortcutPath LaunchOptions IsHidden AllowDesktopConfig AllowOverlay openvr Devkit DevkitGameID LastPlayTime tags 0 Shortcuts 1 Amazon 2 All Games 6 appid ¢‡ æappname Adam Wolfe Exe "R:\Amazon Games\Library\Adam Wolfe\AdamWolfe.exe" StartDir "R:\Amazon
我注意到,我添加到 Steam 的游戏名称总是位于appname
和这两个单词之间Exe
,如下所示:
£á@¤appname A Good Snowman Is Hard To Build Exe
我如何修改/提取该文件中的条目,以便得到一个包含每行一个单元格之间的条目的 Excelappname
表Exe
?
就像这样:
我可能在考虑正则表达式?我没有 Microsoft Office,只有 Libre Office
答案1
这应该很容易实现,您只需要 PowerShell,不需要 Office Suite。
从路径的语法来看,它们以驱动器号(即 T:)开头,并使用反斜杠(\
)分隔目录,我假设您使用的是 Windows。
如果您使用的是 Windows,请使用以下命令打开 PowerShell:
Win+ →R类型PowerShell
→ Ctrl++ShiftEnter
然后,一旦您进入 PowerShell,您就可以使用这个正则表达式来获取游戏名称:(appname)(.*?)(Exe)
Steam .vdf 格式是一种专有格式,因此很难正确解析该格式,但您可能可以使用以下格式:GitHub:Steam-GetOnTop,我不使用Steam,也没有用过包裹,所以我不保证它能起作用。
appname
但由于该文件是明文编码的,你可以用 notepad.exe 打开它,而你需要的只是和之间的内容Exe
,你可以使用暴力破解的方式:
$shortcuts=((Get-Content "C:\Program Files (x86)\Steam\userdata\149576161\config\shortcuts.vdf").ToCharArray() | where{![char]::IsControl("$_")}) -join ""
($shortcuts | Select-String -pattern "(appname)(.*?)(Exe)" -AllMatches).matches.value | %{$_.Substring(7,$_.Length-10)} | sort-object | out-file "$([environment]::getfolderpath('desktop'))\gamename.txt"
解释:
步骤 1:我们获取 Shortcuts.vdf 的内容,将字符串转换为字符数组,过滤掉控制字符,并连接成字符串
第 2 步:我们打印在第一步中获得的内容并将其传递到管道;我们从管道中获取内容并选择所有匹配的字符串(appname)(.*?)(Exe)
,并使用().matches.value
仅获取匹配的字符串,然后我们摆脱appname
和Exe
并将其导出到位于桌面上的名为 gamename.txt 的文件中。
但是,如果您坚持要导出到只有一列的 Excel 表,请使用以下命令:
$shortcuts=((Get-Content "C:\Program Files (x86)\Steam\userdata\149576161\config\shortcuts.vdf").ToCharArray() | where{![char]::IsControl("$_")}) -join ""
($shortcuts | Select-String -pattern "(appname)(.*?)(Exe)" -AllMatches).matches.value | %{$_.Substring(7,$_.Length-10)} | sort-object | %{[PSCustomObject]@{GameName=$_}} | export-csv "$([environment]::getfolderpath('desktop'))\gamename.csv"
该方法与第一种方法几乎相同,但是我们将结果转换为[PSCustomObject]
并将最终结果导出到位于路径“C:\Users\USERNAME\Desktop\gamename.csv”的.csv 文件,而不是.txt 文件。
进一步说明:
Microsoft Docs:什么是 PowerShell?
Microsoft Docs:关于 PSCustomObject 你想要知道的一切
输出
如你所说它不包含任何个人信息并将文件附加到问题中,我将在下面发布我得到的结果:
Google Drive
我的第一个方法的输出:
我的第二种方法的输出:
来自其他答案的 vbs 方法的输出:
代码效率分析
第一种方法长度为 358 个字符
第二种方法长度为 396 个字符
该 vbs 脚本长度为 541 个字符
第一种方法打高尔夫球:
$s=((gc "C:\Program Files (x86)\Steam\userdata\149576161\config\shortcuts.vdf").ToCharArray() | ?{![char]::IsControl("$_")}) -join ""
($s | sls "(appname)(.*?)(Exe)" -all).matches.value | %{$_.Substring(7,$_.Length-10)} | sort >"$([environment]::getfolderpath('desktop'))\gamename.txt"
286 个字符长
第二种方法打高尔夫球:
$s=((gc "C:\Program Files (x86)\Steam\userdata\149576161\config\shortcuts.vdf").ToCharArray() | ?{![char]::IsControl("$_")}) -join ""
($s | sls "(appname)(.*?)(Exe)" -all).matches.value | %{$_.Substring(7,$_.Length-10)} | sort | %{[PSCustomObject]@{GameName=$_}} | epcsv "$([environment]::getfolderpath('desktop'))\gamename.csv"
329 个字符长
vbs不会对结果进行排序,也不会自动输出到文件。
以下是运行时测试:
PS C:\Users\Estranger> measure-command {$s=((gc "C:\Users\Estranger\Downloads\shortcuts.vdf").ToCharArray() | ?{![char]::IsControl("$_")}) -join ""
>> ($s | sls "(appname)(.*?)(Exe)" -all).matches.value | %{$_.Substring(7,$_.Length-10)} | sort >"$([environment]::getfolderpath('desktop'))\gamename.txt"}
Days : 0
Hours : 0
Minutes : 0
Seconds : 1
Milliseconds : 86
Ticks : 10863660
TotalDays : 1.25736805555556E-05
TotalHours : 0.000301768333333333
TotalMinutes : 0.0181061
TotalSeconds : 1.086366
TotalMilliseconds : 1086.366
PS C:\Users\Estranger> measure-command {$s=((gc "C:\Users\Estranger\Downloads\shortcuts.vdf").ToCharArray() | ?{![char]::IsControl("$_")}) -join ""
>> ($s | sls "(appname)(.*?)(Exe)" -all).matches.value | %{$_.Substring(7,$_.Length-10)} | sort | %{[PSCustomObject]@{GameName=$_}} | epcsv "$([environment]::getfolderpath('desktop'))\gamename.csv"}
Days : 0
Hours : 0
Minutes : 0
Seconds : 1
Milliseconds : 171
Ticks : 11716488
TotalDays : 1.356075E-05
TotalHours : 0.000325458
TotalMinutes : 0.01952748
TotalSeconds : 1.1716488
TotalMilliseconds : 1171.6488
PS C:\Users\Estranger> measure-command {cscript steam.vbs}
Days : 0
Hours : 0
Minutes : 0
Seconds : 1
Milliseconds : 338
Ticks : 13380483
TotalDays : 1.54866701388889E-05
TotalHours : 0.000371680083333333
TotalMinutes : 0.022300805
TotalSeconds : 1.3380483
TotalMilliseconds : 1338.0483
我的第一个方法显然获胜了,这个负面评价是不公平的,原贴作者没有发布关键信息,所以无论我写得多好,我的代码都无法一开始就解决原贴作者的问题。
对于那些对我的答案投反对票的人,我的答案现在已被编辑,并且你的反对票不再被锁定,请根据客观事实谨慎投票,并请取消反对票。
对于原帖作者,客观地说,另一个答案不如我的答案有效。你一开始没有发布正确的信息,所以我只有错误的信息可以使用。无论我有多好,我都不可能解决你的问题,因为“垃圾进,垃圾出”,另一个回答者只是运气好,在我不在的时候你发布正确的信息时碰巧在线,而且你看到脚本不如我的好,请考虑接受我的答案。
更短的代码:
方法 1 打高尔夫
$s=((gc "C:\Program Files (x86)\Steam\userdata\149576161\config\shortcuts.vdf").ToCharArray() | ?{![char]::IsControl("$_")}) -join ""
($s | sls "(?<=appname)(.*?)(?=Exe)" -All).matches.value | sort > "$([environment]::getfolderpath('desktop'))\gamename.txt"
方法 2 打高尔夫球
$s=((gc "C:\Program Files (x86)\Steam\userdata\149576161\config\shortcuts.vdf").ToCharArray() | ?{![char]::IsControl("$_")}) -join ""
($s | sls "(?<=appname)(.*?)(?=Exe)" -All).matches.value | sort | %{[PSCustomObject]@{GameName=$_}} | epcsv "$([environment]::getfolderpath('desktop'))\gamename.csv"
这些新方法使用的正则表达式将仅匹配游戏名称,但与第一个正则表达式相比,使用它会使运行时间增加 0.3 秒。
这是我使用第二种方法 1 打高尔夫球时在控制台中看到的内容:
我想回答这个问题并帮助原帖者,仅仅是因为我觉得这个问题很有趣,原帖者想要的东西很有趣,我只是编程方面的初学者,我想通过回答别人用程序发布的问题来提高我的技能,仅此而已...
PS我真的想成为一名黑客......
答案2
这是一段非常老套的 vbscript,它本质上将文件分解shortcuts.vdf
为单独的行(基于字符nul
),然后在调用的行之后输出下一行AppName
。我确实尝试过file
使用Chr(0)
分隔符进行拆分,但它引发了一个错误。
将以下文件另存为steam.vbs
:
Dim oFile: Set oFile = CreateObject("Scripting.FileSystemObject").GetFile("shortcuts.vdf")
Dim file : file = oFile.OpenAsTextStream().Read(oFile.Size)
Dim content, i, c
For i = 1 To Len(file)
c = Asc(Mid(file, i, 1))
If c = 0 Then
content = content & VbCrLf
ElseIf c > 3 Then
content = content & Chr(c)
end If
Next
Dim items : items = Split(content, VbCrLf)
For i = 0 To Ubound(items)
If Instr(LCase(items(i)), "appname") > 0 Then Wscript.Echo items(i+1)
Next
Set oFile = Nothing
将文件复制shortcuts.vcf
到与此代码相同的文件夹中,然后从命令行类型:
cscript steam.vbs > output.txt
在任何文本编辑器中打开输出,您将看到已添加的所有游戏快捷方式的列表。
答案3
有一个非官方的vdf python 模块用于解析 Valve 的 vdf 文件。有了它,解析和写出包含所有名称的 csv 非常简单:
import vdf
def print_names(shortcut_file_path):
with open(shortcut_file_path, "rb") as f:
shortcuts = vdf.binary_load(f)
with open('c:/scratch/shortcuts.csv', 'w') as f:
f.write('Game Name\n')
for k, v in shortcuts["shortcuts"].items():
appname = v.get("appname")
print(appname)
f.write(appname + '\n')
print_names("C:/Program Files (x86)/Steam/userdata/149576161/config/shortcuts.vdf")
要查看所有可用字段(“appname”除外),请将以下print
行替换为import pprint; pprint.pprint(v); break
与其他 Python 模块一样,使用 安装 vdf pip install vdf
。