首页 > 解决方案 > Python:将复杂的类似列表的字符串转换为列表(没有用于混合字节和非字节文字的 SyntaxError)

问题描述

我有这个字符串:u'''[ "A", """B""",'C' , " D"]'''

我想得到一个类似的列表:["A","""B""",'C'," D"]*edited as ""B"" 确实无效

有些使用“”“”或''来逃避'或“或两者兼而有之。

我尝试了各种解决方案: 将列表的字符串表示形式转换为列表

s= u'''[ "A", """B""",'C' , " D"]'''

1)

import numpy as np
List = np.array(s).tolist()
print(List)
print(type(List))

结果 :

[ "A", """B""",'C' , " D"]
<class 'str'>

所以不是一个列表,然后:

2)

import ast
List = ast.literal_eval(s)
print(List)
print(type(List))

结果

SyntaxError: cannot mix bytes and nonbytes literals

任何其他想法如何做到这一点?(我正在使用 Python 3.6)

我想到了正则表达式或简单的字符串替换,但是当我想到所有我们逃避替换列表元素中的“,”等字符的情况时,它变得非常复杂

标签: pythonstringlistnumpy

解决方案


ast.literal_eval(s)只要不存在未转义的引号,使用效果就很好。我们可以使用正则表达式来解决这个问题。不幸的是,这并不容易。但是,我为你找到了灵丹妙药。

首先,让我们识别所有具有相同类型内引号的带引号的子字符串:

(?<=[\[,])\s*(['\"])(?:(\1)|.)*?\1(?=\s*[,\]])

解释

  • (?<=[\[,])使用lookbehind的前锚:列表或前一个列表元素的开头:[,
  • \s*零个或多个空格
  • (['\"])捕获组中的开盘报价
  • (?:(\1)|.)*?$1懒惰匹配任何字符,如果与放入相同的引号$2
  • \1使用反向引用结束报价
  • (?=\s*[,\]])使用前瞻的后锚:,]

演示

现在,真正的乐趣开始了。接下来,在具有相同类型的内引号的子字符串中搜索未转义的引号。

为此,我使用Skip_what_to_avoid方法和一些高级正则表达式技术,例如展开循环

^"[^"\\]*(?:\\.[^\"\\]*)*"$|^'[^'\\]*(?:\\.[^'\\]*)*'$|^\s*["'][^"'\\]*|["']\s*$|\\.|(["'])[^"'\\\n]*

这里的关键思想是完全忽略正则表达式引擎返回的整体匹配:$0是垃圾桶。相反,我们只需要检查 capture group $1,它在设置时包含我们正在寻找的内容。基本上,所有这些未捕获的交替匹配用引号括起来的常规、正确转义的子字符串。我们只想要未转义的。

演示2

我决定重新组合原始字符串,并\添加必要的内容以正确转义那些未转义的引号。当然还有其他方法。

最后,将完整的示例代码放在一起(在线演示):

import re
import ast

test = ("[ \"A\", \"\"B\"\",'C' , \" D\"]\n"
    "[ \"A\", \"'B'\",'C' , \" D\"]\n"
    "[ \"A\", ''B'','C' , \" D\"]\n"
    "[ \"A\", '\"B\"','C' , \" D\"]\n"
    "[ \"A\", '8 o'clock','C' , \" D\"]\n"
    "[ \"A\", \"Ol' 8 o'clock\",'C' , \" D\"]\n"
    "[\"Some Text\"]\n"
    "[\"Some more Text\"]\n"
    "[\"Even more text about \\\"this text\\\"\"]\n"
    "[\"Ol' 8 o'clock\"]\n"
    "['8 o'clock']\n"
    "[ '8 o'clock']\n"
    "['Ol' 8 o'clock']\n"
    "[\"\"B\"]\n"
    "[\"\\\"B\"]\n"
    "[\"\\\\\"B\"]\n"
    "[\"\\\\\\\"B\"]\n"
    "[\"\\\\\\\\\"B\"]")
result = u''
last_index = 0
regex1 = r"(?<=[\[,])\s*(['\"])(?:(\1)|.)*?\1(?=\s*[,\]])" #nested quotes of the same type
regex2 = r'''^"[^"\\]*(?:\\.[^\"\\]*)*"$|^'[^'\\]*(?:\\.[^'\\]*)*'$|^\s*["'][^"'\\]*|["']\s*$|\\.|(["'])[^"'\\\n]*''' # unescaped quotes in $1
matches = re.finditer(regex1, test, re.MULTILINE)
for match in matches:
    if match.groups()[1] is not None: #nested quotes of the same type present
        print(match.group())
        inner = re.finditer(regex2, match.group())
        for m in inner:            
            if m.groups()[0] is not None: # unescaped quotes in $1 present
                result += test[last_index:match.start() + m.start()] + '\\' + m.group()
                last_index = match.start()+m.end()
result += test[last_index:len(test)]
print(result)

for test_str in result.split("\n"):
    List = ast.literal_eval(test_str)
    print(List)
    print(type(List))

我们有一个测试字符串,其中包含多个列表作为具有各种特殊情况的字符串文字:不同和相同类型的引号、转义引号、转义转义序列等。使用上面概述的解决方案清除测试字符串split,然后单独使用 转换为列表literal_eval

这可能是迄今为止我制作的最先进的正则表达式解决方案之一。


推荐阅读