在 Python 中将文本表转换为 CSV

Posted

技术标签:

【中文标题】在 Python 中将文本表转换为 CSV【英文标题】:Converting Text Tables Into CSVs in Python 【发布时间】:2021-01-31 05:10:15 【问题描述】:

我希望将表格数据转换为 CSV,但是当表格中包含某些缺失值的行时,我遇到了障碍。输入如下表,

systemd       1                   root  cwd       DIR                8|1      4096          2 /
systemd       1                   root  rtd       DIR                8|1      4096          2 /
systemd       1                   root  txt       REG                8|1   1612152     101375 /lib/systemd/systemd
systemd       1                   root  mem       REG                8|1   1700792      26009 /lib/x86_64-linux-gnu/libm-2.27.so
systemd       1                   root  mem       REG                8|1    121016       1715 /lib/x86_64-linux-gnu/libudev.so.1.6.9
node        697   698             user1 cwd       DIR               8|33      4096    7995393 /home/user1
node        697   698             user2 rtd       DIR                8|1      4096          2 /
node        697   698             user1 txt       REG               8|33  43680144    8003081 /home/user1/.vscode-server/bin/26076a4de974ead31f97692a0d32f90d735645c0/node
node        697   698             user1 mem       REG                8|1    101168      26021 /lib/x86_64-linux-gnu/libresolv-2.27.so
node        697   698             user1 mem       REG                8|1     26936      26014 /lib/x86_64-linux-gnu/libnss_dns-2.27.so

我想将其转换为保留列数的 CSV,输出应如下所示,

systemd,1,,root,cwd,DIR,8|1,4096,2,/
systemd,1,,root,rtd,DIR,8|1,4096,2,/
systemd,1,,root,txt,REG,8|1,1612152,101375,/lib/systemd/systemd
systemd,1,,root,mem,REG,8|1,1700792,26009,/lib/x86_64-linux-gnu/libm-2.27.so
systemd,1,,root,mem,REG,8|1,121016,1715,/lib/x86_64-linux-gnu/libudev.so.1.6.9
node,697,698,user1,cwd,DIR,8|33,4096,7995393,/home/user1
node,697,698,user2,rtd,DIR,8|1,4096,2,/
node,697,698,user1,txt,REG,8|33,43680144,8003081,/home/user1/.vscode-server/bin/26076a4de974ead31f97692a0d32f90d735645c0/node
node,697,698,user1,mem,REG,8|1,101168,26021,/lib/x86_64-linux-gnu/libresolv-2.27.so
node,697,698,user1,mem,REG ,8|1,26936,2601,/lib/x86_64-linux-gnu/libnss_dns-2.27.so

到目前为止,我已经尝试使用 pandas read_fwf 函数,然后将其转换为 CSV,但它没有评估缺失的列值。因此,我没有为 CSV 中的每一行获取 10 个值,而是仅获取可见的 9。使用 pandas read_table 函数时也会发生同样的事情。我也尝试过使用正则表达式模式,但我不希望表格格式每次都相同,升级代码以合并更多表格成为一个问题

高度赞赏任何解决此问题的方法。非常感谢!

【问题讨论】:

您是否尝试将"" 替换为"NaN"np.nan 或任何文本? 当表格有列标题时不会出现此问题,当输入表格没有列标题时会导致此问题。 @woblob 并非如此,这会用提供的文本替换每个空格,在某些情况下,单列中的值也可以有空格(例如描述列) 如果没有标题,您尝试pd.read_table(filename, header=None)header=0 吗? @woblob 我刚刚做了,添加了header=None 部分,它像以前一样跳过了缺失值 【参考方案1】:

您可以通过将数据拆分为有效行和无效行来缩小问题。有效行将具有预期的列数,而无效行将缺少一列或多列。不确定是否可以在不知道列之间的确切分隔符的情况下完全自动化此操作。

您提到空格可以出现在描述列中。您无法真正区分 user1 cwd ,它们是两个单独的列和单个列内的空格。这样的行将被放入invalid 列表中,除非它们碰巧有一个缺失值来“平衡”它。它非常脆弱,所以最好确保你有一个正确的分隔符,或者至少你的列值中没有空格。

from io import StringIO
import pandas as pd
import re

data = StringIO("""
systemd       1                   root  cwd       DIR                8|1      4096          2 /
systemd       1                   root  rtd       DIR                8|1      4096          2 /
systemd       1                   root  txt       REG                8|1   1612152     101375 /lib/systemd/systemd
systemd       1                   root  mem       REG                8|1   1700792      26009 /lib/x86_64-linux-gnu/libm-2.27.so
systemd       1                   root  mem       REG                8|1    121016       1715 /lib/x86_64-linux-gnu/libudev.so.1.6.9
node        697   698             user1 cwd       DIR               8|33      4096    7995393 /home/user1
node        697   698             user2 rtd       DIR                8|1      4096          2 /
node        697   698             user1 txt       REG               8|33  43680144    8003081 /home/user1/.vscode-server/bin/26076a4de974ead31f97692a0d32f90d735645c0/node
node        697   698             user1 mem       REG                8|1    101168      26021 /lib/x86_64-linux-gnu/libresolv-2.27.so
node        697   698             user1 mem       REG                8|1     26936      26014 /lib/x86_64-linux-gnu/libnss_dns-2.27.so
""")

valid_rows = []
invalid_rows = []
num_of_columns = 10

for line in data.readlines():
    # note that in your data there is a new line
    # at the end of each line which is also captured by \s
    if len(re.findall(r"\s+", line)) == num_of_columns:
        valid_rows.append(line)
    else:
        invalid_rows.append(line)        

df = pd.read_csv(StringIO("".join(valid_rows)), delim_whitespace=True, names=range(10))

【讨论】:

感谢解答,但是这个方法完全丢弃了无效行,我真的做不到。如果一列在所有行中都是空白的,它将把整个数据归类为无效。生成此数据的来源超出了我的控制范围,因此我无法在数据中添加分隔符,而无需在来源处进行更改或实际查看数据。 不完全——它只是将无效行分开,您可以单独保存并手动修复。我猜列的列宽也不是固定的,你不能提前知道第 1 列的最大宽度是 7? 是的,没有办法事先知道。 那恐怕只是杂乱无章的数据,不太适合自动化。您所能做的就是通过获取好的行并手动修复其余行来减轻痛苦(如果您使用带有“列模式”的文本编辑器,这可能不会那么糟糕)。

以上是关于在 Python 中将文本表转换为 CSV的主要内容,如果未能解决你的问题,请参考以下文章

将文本表的数据导出为 .csv (Tableau)

修剪文本表并将值存储为变量

如何在 Tableau Online 中删除文本表上的水平线?

如何在 SWIG 中将向量的锯齿状 C++ 向量转换(类型映射)为 Python

Tableau - YTD,MTD 作为文本表中的列

如何在预填充的文本区域中将 <br /> 转换为换行符