使用Python编码解析CS:GO语言文件
问题内容:
本主题与以Python主题解析CS:GO脚本文件有关,但是还有另一个问题。我正在处理CS:GO上的内容,现在我正在尝试制作一个python工具,将所有数据从/ scripts
/文件夹导入Python词典。
解析数据后的下一步是从/ resources解析语言资源文件,并在字典和语言之间建立关系。
有一个用于Eng本地化的原始文件:https
:
//github.com/spec45as/PySteamBot/blob/master/csgo_english.txt
文件格式类似于上一个任务,但是我遇到了另一个问题。所有语言文件均采用UTF-16-LE编码,我无法理解在Python中使用编码文件和字符串的方式(我主要使用Java),我曾尝试基于提出一些解决方案open(fileName, encoding='utf-16-le').read()
,但我不知道如何在pyparsing中使用这种编码的字符串。
pyparsing.ParseException:期望的带引号的字符串,以“以”结尾(以char 0开头),(行:1,列:1)
另一个问题是带有类似\“的表达式的行,例如:
"musickit_midnightriders_01_desc" "\"HAPPY HOLIDAYS, ****ERS!\"\n -Midnight Riders"
如果我想保留这些行,如何解析这些符号?
问题答案:
此输入文件有一些新的皱纹,而原始CS:GO示例中没有这些皱纹:
\"
在某些值字符串中嵌入转义引号- 一些带引号的值字符串跨越多行
- 一些值以结尾的环境条件结尾(例如
[$WIN32]
,[$OSX]
) - 在文件中嵌入注释,标记为“ //”
通过修改的定义来解决前两个问题value_qs
。由于值现在比键具有更全面的功能,因此我决定为其使用单独的QuotedString定义:
key_qs = QuotedString('"').setName("key_qs")
value_qs = QuotedString('"', escChar='\\', multiline=True).setName("value_qs")
第三个需要更多的重构。这些限定条件的使用类似于#IFDEF
C中的宏-仅当环境符合条件时,它们才启用/禁用定义。其中一些条件甚至是布尔表达式:
[!$PS3]
[$WIN32||$X360||$OSX]
[!$X360&&!$PS3]
这可能会导致定义文件中的键重复,例如以下几行:
"Menu_Dlg_Leaderboards_Lost_Connection" "You must be connected to Xbox LIVE to view Leaderboards. Please check your connection and try again." [$X360]
"Menu_Dlg_Leaderboards_Lost_Connection" "You must be connected to PlayStation®Network and Steam to view Leaderboards. Please check your connection and try again." [$PS3]
"Menu_Dlg_Leaderboards_Lost_Connection" "You must be connected to Steam to view Leaderboards. Please check your connection and try again."
其中包含3个键“ Menu_Dlg_Leaderboards_Lost_Connection”的定义,具体取决于所设置的环境值。
为了在解析文件时不会丢失这些值,我选择了在解析时通过附加条件(如果存在)来修改密钥。这段代码实现了更改:
LBRACK,RBRACK = map(Suppress, "[]")
qualExpr = Word(alphanums+'$!&|')
qualExprCondition = LBRACK + qualExpr + RBRACK
key_value = Group(key_qs + value + Optional(qualExprCondition("qual")))
def addQualifierToKey(tokens):
tt = tokens[0]
if 'qual' in tt:
tt[0] += '/' + tt.pop(-1)
key_value.setParseAction(addQualifierToKey)
这样,在上面的示例中,您将获得3个键:
- Menu_Dlg_Leaderboards_Lost_Connection / $ X360
- Menu_Dlg_Leaderboards_Lost_Connection / $ PS3
- Menu_Dlg_Leaderboards_Lost_Connection
最后,可能是最简单的评论处理。Pyparsing内置了对跳过注释的支持,就像空白一样。您只需要定义注释的表达式,并让顶级解析器忽略它。为了支持此功能,在pyparsing中预定义了几种常见的注释形式。在这种情况下,解决方案只是将最终解析器的定义更改为:
parser.ignore(dblSlashComment)
最后,最后是QuotedString的实现中存在一个小错误,在该错误中,标准的空白字符串文字(例如\t
和)\n
未处理,仅被视为不必要的转义“
t”或“ n”。所以现在,当这一行被解析时:
"SFUI_SteamOverlay_Text" "This feature requires Steam Community In-Game to be enabled.\n\nYou might need to restart the game after you enable this feature in Steam:\nSteam -> File -> Settings -> In-Game: Enable Steam Community In-Game\n" [$WIN32]
对于值字符串,您只会得到:
This feature requires Steam Community In-Game to be enabled.nnYou
might need to restart the game after you enable this feature in
Steam:nSteam -> File -> Settings -> In-Game: Enable Steam Community
In-Gamen
代替:
This feature requires Steam Community In-Game to be enabled.
You might need to restart the game after you enable this feature in Steam:
Steam -> File -> Settings -> In-Game: Enable Steam Community In-Game
我将不得不在下一版pyparsing中修复此行为。
这是最终的解析器代码:
from pyparsing import (Suppress, QuotedString, Forward, Group, Dict,
ZeroOrMore, Word, alphanums, Optional, dblSlashComment)
LBRACE,RBRACE = map(Suppress, "{}")
key_qs = QuotedString('"').setName("key_qs")
value_qs = QuotedString('"', escChar='\\', multiline=True).setName("value_qs")
# use this code to convert integer values to ints at parse time
def convert_integers(tokens):
if tokens[0].isdigit():
tokens[0] = int(tokens[0])
value_qs.setParseAction(convert_integers)
LBRACK,RBRACK = map(Suppress, "[]")
qualExpr = Word(alphanums+'$!&|')
qualExprCondition = LBRACK + qualExpr + RBRACK
value = Forward()
key_value = Group(key_qs + value + Optional(qualExprCondition("qual")))
def addQualifierToKey(tokens):
tt = tokens[0]
if 'qual' in tt:
tt[0] += '/' + tt.pop(-1)
key_value.setParseAction(addQualifierToKey)
struct = (LBRACE + Dict(ZeroOrMore(key_value)) + RBRACE).setName("struct")
value <<= (value_qs | struct)
parser = Dict(key_value)
parser.ignore(dblSlashComment)
sample = open('cs_go_sample2.txt').read()
config = parser.parseString(sample)
print (config.keys())
for k in config.lang.keys():
print ('- ' + k)
#~ config.lang.pprint()
print (config.lang.Tokens.StickerKit_comm01_burn_them_all)
print (config.lang.Tokens['SFUI_SteamOverlay_Text/$WIN32'])