使用Python

时间:2016-03-12 07:33:53

标签: python parsing encoding utf-8

此主题与Parsing a CS:GO script file in Python主题相关,但还有另一个问题。 我正在处理来自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:预期的带引号的字符串,以&#34开头;   以"结尾(在char 0),(line:1,col:1)

另一个问题是具有\"类似表达式的行,例如:

"musickit_midnightriders_01_desc"       "\"HAPPY HOLIDAYS, ****ERS!\"\n    -Midnight Riders"

如果我想保留这些符号,如何解析这些符号?

1 个答案:

答案 0 :(得分:1)

这个输入文件有一些新的皱纹不在原始的CS:GO示例中:

  1. 在某些值字符串中嵌入\"个转义引号
  2. 部分引用的值字符串跨越多行
  3. 某些值以尾随环境条件结束(例如[$WIN32][$OSX]
  4. 文件中的嵌入式评论,标有“//”
  5. 通过修改value_qs的定义来解决前两个问题。由于值现在比键更全面,我决定对它们使用单​​独的QuotedString定义:

    key_qs = QuotedString('"').setName("key_qs")
    value_qs = QuotedString('"', escChar='\\', multiline=True).setName("value_qs")
    

    第三个需要更多的重构。这些限定条件的使用类似于C中的#IFDEF宏 - 只有在环境与条件匹配时才启用/禁用定义。其中一些条件甚至是布尔表达式:

    • [!$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'])