【问题标题】:Replace repeating delimiters in a text file with an alternate character用替换字符替换文本文件中的重复分隔符
【发布时间】:2017-01-12 05:04:17
【问题描述】:

我正在尝试处理一个大管道“|”分隔的双引号限定文本文件(>700,000 条记录,每条记录 >3,000 个字符,每条记录 28 个字段)。使用 python 脚本。我遇到了一个问题,因为 csv 解析器由于未转义的双引号字符和嵌入在文件中字段的文本中的管道而错误地解析了字段。由于文件中不存在制表符,我想通过用制表符 (\t) 替换双引号管道双引号分隔符/限定符字符序列 ("|") 将其转换为制表符分隔文件。如果填充了每个字段但有些字段没有填充,这将相对简单。未填充的字段由空字符串表示,因此我可以按顺序使用 1 到 7 个以双引号开头的管道分隔符。

一个简单的例子是:

"abc"|"2016-07-30"|"text narrative field"|"2016-08-01"|"123"|"456"|"789"|"EOR"

一个比较有代表性的例子是:

"abc"|"2017-01-01"|"height: 5' 7" (~180 cm) | weight: 80kg | in good health"|"2016-01-10"||||"EOR"

我一直在尝试编写一个正则表达式,它将每个管道字符/双引号组合或管道字符序列立即替换为带有 TAB 字符的双引号 1 对 1。我发现了许多用单个字符替换重复字符串的正则表达式示例,但没有一个用等长字符串替换一系列重复字符的替代字符。

我尝试了以下正则表达式:"\|{1,}",它适用于单个管道字符,但会用单个 TAB 依次替换多个管道。我还需要处理以下相关方面:

  • 删除行首/双引号 (^")
  • 删除双引号/行尾 ("$)
  • 并将双引号/竖线(1 个或多个)/行尾(例如“\|$)替换为与竖线字符相同数量的 TAB 字符

应用正则表达式后的输出记录如下所示,使用 \t 表示 TAB 字符:

abc\t2016-07-30\ttext narrative field\t2016-08-01\t123\t456\t789\tEOR
abc\t2017-01-01\theight: 5' 7" (~180 cm) | weight: 80kg | in good health\t2016-01-10\t\t\t\tEOR

我愿意在 python 或 linux 中使用 sed 或 awk 解决这个问题

【问题讨论】:

  • csv 解析器不正确地解析字段 - “祈祷,巴贝奇先生,如果你把错误的数字输入机器,正确的答案会出来吗?”。如果您的文件没有有效的逗号分隔值,那么将错误的输出归咎于 csv 解析器有点苛刻:P
  • 这取决于解析器。例如,Informatica 可以正确解析上面提供的更复杂的记录。但是,至少在我看来,csv 解析器没有。我预计它会将其解析为:field 1: abcfield 2: 2017-01-01field 3: height: 5' 7" (~180 cm) | weight: 80kg | in good healthfield 4: 2016-01-10……但是,它会将其解析为:field 1: abcfield 2: 2017-01-01field 3: height: 5' 7" (~180 cm)field 4: weight: 80kgfield 5: in good health@9876543即使在 | 之前还有其他字符,也会在 " 之后中断

标签: python regex linux


【解决方案1】:
import re

def count_pipes_in_regex_match(m):
  #  regex capture group should only contain pipe chars
  matched_pipes = m.groups()[0]

  return '\t' * len(matched_pipes)


# test string
s='"abc"|"2017-01-01"|"height: 5\' 7" (~180 cm) | weight: 80kg | in good health"|"2016-01-10"||||"EOR"'


# replace leading or trailing quotes
s = re.sub('^"|"$', '', s)

# replace quote pipe(s) quote 
# or      quote pipe(s) end-of-string
# with as many tabs as there were pipes
s = re.sub('"(\|+)("|$)', count_pipes_in_regex_match, s)

print repr(s) #repr to show the tabs

试试online at repl.it

【讨论】:

  • 非常迅速而优雅的回答。谢谢!
【解决方案2】:

既然您正在寻找"|" 不是用|""| 替换多个|| 的答案吗?

怎么样:

while True:
    new_data = re.sub(r'\|\|', '|""|', data)
    if data == new_data:
        break
    data = new_data

在此之后,您可以将"|" 替换为制表符。

【讨论】:

  • 感谢您的想法。我认为在这种情况下它不会起作用,因为在引号限定的文本字段中嵌入了多个顺序管道。例如。 “体重|||需要更多的液体”。幸运的是,在文本字段中没有直接在管道前面或后面的带有双引号的字符序列。
  • 存在倍数的事实是我的示例有循环的原因。您应该尝试一下,在我的示例运行后,您希望用制表符替换 "|" 的正则表达式应该可以正常工作。
  • 我会试一试并发布结果。谢谢。
【解决方案3】:

你可以在 3 遍中完成。

  1. 将所有|| 替换为|""|
  2. "|" 上拆分(和| 在两端)
  3. 从每个字段中删除引号。

如下:

import re

for line in file:
    while '||' in line:
        line = line.replace('||', '|""|')

    fields = re.split('^\||\|$|"\|"', line)

    new_line = '\t'.join([field.strip('"') for field in fields])

【讨论】:

  • 我不认为这会起作用,因为双引号限定的文本字段中嵌入了管道,即我不想将带有嵌入管道的单个文本字段解析为单独的字段.
  • 非常正确,我已经更新了代码以支持引号中的管道。
猜你喜欢
  • 2019-01-09
  • 1970-01-01
  • 2013-04-05
  • 1970-01-01
  • 2020-01-04
  • 1970-01-01
  • 1970-01-01
  • 2017-04-03
  • 2011-11-21
相关资源
最近更新 更多