使用正则表达式或常规 Python 进行字符串替换?
Posted
技术标签:
【中文标题】使用正则表达式或常规 Python 进行字符串替换?【英文标题】:String substitution with regex or regular Python? 【发布时间】:2018-07-06 16:31:05 【问题描述】:我有一个字符串列表,如下所示
orig = ["a1 2.3 ABC 4 DEFG 567 b890",
"a2 3.0 HI 4 5 JKL 67 c65",
"b1 1.2 MNOP 3 45 67 89 QR 987 d64 e112"]
这里的上下文是,这是一个 CSV 文件,并且省略了某些列。我不认为 pandas csv 阅读器可以处理这些情况。现在的想法是为缺失值注入na
,因此输出变为
corr = ["a1 2.3 ABC 4 na na na DEFG 567 b890",
"a2 3.0 HI 4 5 na na JKL 67 c65",
"b1 1.2 MNOP 3 45 67 89 QR 987 d64 e112"]
稍后在导入 pandas 时将第二列与大写单词对齐。
结构如下:列之间的分隔符是两个或多个空格,两个大写列之间的分隔符必须是四个值。在原始文件中,总是只有两个大写列,它们之间至少有一个和最多四个数字,并且这些大写单词之间只有数字值。 我可以毫无问题地用原生 Python 编写脚本,所以请不要对此提出任何建议。但我认为,这可能是正则表达式的情况。作为一个正则表达式初学者,我只设法用
提取两个大写列之间的字符串for line in orig:
a = re.findall("([A-Z]+[\s\d]+[A-Z]+)", line))
print(a)
>>>'ABC 4 DEFG' #etc pp
现在在正则表达式中是否有一种简单的方法来确定大写单词之间有多少个数字并插入“na”值以使其之间始终具有四个值?还是应该用原生 Python 来做?
当然,如果有办法使用 pandas csv 阅读器做到这一点,那就更好了。但是我研究了pandas csv_reader docs,并没有发现任何有用的东西。
【问题讨论】:
使用简单的python,这对正则表达式来说并不容易——你需要做一些回溯和分组,也许在替换之前插入假人——如果可能的话,我对此表示怀疑。正则表达式在这里是错误的工具。 在阅读了 re.module 手册并查看了关于 SO 的正则表达式问题后,我也有这种印象。但我经常对人们的所作所为感到惊讶,所以我想我会问。 如果大写单词之间已经有超过 4 个数字,或者只是“ABC DEF GHI JKL MNO”的文本,会发生什么?或者,如果在大写字母之间混合了并非全部大写的数字和代码?除了大写单词分隔符之间应该有“4个单词”之外,还有其他标准吗? @Piinthesky 好的 - 谢谢。因此,如果您不反对使用re.findall
识别字符串之间的部分 - 您可以捕获一些组并在re.sub
中使用可调用的替换函数?我刚刚尝试过:re.sub(r'\s2,([A-Z]+)\s2,(.*?)\s2,([A-Z]+)\b', lambda m: ' '.join((m.group(1), 'na' * 4, m.group(3))), orig[1])
- 这需要工作,但可能有用...
@Piinthesky 只是一直在努力帮助人们学习...您是否有 我可以毫无问题地用原生 Python 编写脚本的示例?在这一点上 - 我认为您实际上已经可以使用其中的一些,只需让正则表达式处理调用替换。
【参考方案1】:
基于完整的 pandas 方法 split 和 concat 可能会有所帮助,即
ndf = pd.Series(orig).str.split(expand=True)
# 0 1 2 3 4 5 6 7 8 9 10
#0 a1 2.3 ABC 4 DEFG 567 b890 None None None None
#1 a2 3.0 HI 4 5 JKL 67 c65 None None None
#2 b1 1.2 MNOP 3 45 67 89 QR 987 d64 e112
df = pd.concat([ndf.iloc[:,:4], ndf.iloc[:,4:].apply(sorted,key=pd.notnull,axis=1)],1)
df.astype(str).apply(' '.join,axis=1).tolist()
['a1 2.3 ABC 4 None None None None DEFG 567 b890',
'a2 3.0 HI 4 None None None 5 JKL 67 c65',
'b1 1.2 MNOP 3 45 67 89 QR 987 d64 e112']
【讨论】:
看来,您的代码用 None 值填充第一个和第二个数字之间的列,因此所有行的长度相同。不幸的是,在第二个大写列之后,行的长度不同,因此这不是所需的输出。但我想我会跟随你的脚步,尝试在 pandas 中做到这一点。感谢您的建议。【参考方案2】:虽然大家一致认为正则表达式并不是这种动态字符串替换的最佳工具,但我发现re
模块在这种情况下使用起来非常舒服。捕获模式基于 Jon Clements 的评论。
import re
orig = ["a1 2.3 ABC 4 DEFG 567 b890",
"a2 3.0 HI 4 5 JKL 67 c65",
"b1 1.2 MNOP 3 45 67 89 QR 987 d64 e112"]
corr = []
for item in orig:
#capture group starting with first capitalised word and stopping before the second
col_betw = re.search("\s2,([A-Z]+.*)\s2,[A-Z]+\s2,", item).group(1)
#determine, how many elements we have in this segment
nr_col_betw = len(re.split(r"\s2,", col_betw))
#substitute, if not enough numbers
if nr_col_betw <= 4:
#fill with NA, which is interpreted by pandas csv reader as NaN
subst = col_betw + " NA" * (5 - nr_col_betw)
item = item.replace(col_betw, subst, 1)
corr.append(item)
【讨论】:
以上是关于使用正则表达式或常规 Python 进行字符串替换?的主要内容,如果未能解决你的问题,请参考以下文章