如何在不依赖文件扩展名的情况下检查文件是CSV、XML还是JSON



我有一个程序,它接受多个输入文件的组合,还可以使用目录来扫描这些输入文件。

通常,我只接受具有CSV、XML和JSON有效文件扩展名的文件,但正如我多年来所了解到的,你不能真正相信用户会提供正确的文件。

例如,如果用户有一个XML扩展名的文件,但实际内容是JSON,我想警告用户并将该文件视为JSON文件。另一种情况是,在基于Unix的操作系统上,文件扩展名不像Windows那样用于识别文件类型,与文件内容相比,这可能会导致更多文件类型不正确的情况。

现在,我的程序收集文件列表,将它们筛选为具有可接受文件扩展名的文件,然后我创建一个concurrent.futures.ThreadPoolExecutor,以便能够同时检查所有文件。实际检查看起来是这样的:

with open(the_file, "r") as f:
check = f.read(1)
if check == "{" or check == "[":
file_type = "json"
elif check == "<":
file_type = "xml"
else:
file_type = "csv"

这有两个主要问题:

  1. 如果文件应该是CSV,并且它的第一个字符是[{<,那么程序将为它分配错误的文件类型
  2. 相反,如果该文件不是任何可接受的类型,并且它不是以[{<开头,则程序将仅假设该文件应该是CSV

我有以下想法来解决这个问题:

import csv
import json
import defusedxml.ElementTree as xml
try:
loaded = xml.parse(the_file)
return "XML"
except xml.ParseError:
del loaded
try:
loaded = json.load(the_file)
return "JSON"
except json.JSONDecodeError:
del loaded
try:
with open(the_file, "r") as csv_file:
loaded = csv.DictReader(csv_file)
return "CSV"
except csv.Error:
print("The file is not in any acceptable format")

问题是,由于我的程序试图同时对多个文件运行此检查,因此同时打开这么多文件,内存使用率可能会非常高。另一个小问题是为了检查文件类型而同时打开和关闭这么多文件的I/O成本。

对于我要做的事情,有没有更有效的替代方案?

这里只是一些小案例的例子,表明你不能只读取一些字符,也不能希望有一个强大的分类:

开头像json,但实际上是一个带有分号分隔符的csv:

[12];[13,14]
...

开始时像xml,但再次是csv

<foo>,<bar>,<fee>
1,2,3
...

以空格开头,但它是一个有效的xml(此处空格记为_(:

__<foo>text...</foo>

IMHO,你能做的最好的事情就是读取文件的第一个字符来决定首先测试什么格式,但是:

  • 您必须至少读取每个文件一次
  • 您必须多次阅读角案例或不正确的文件

XMLJSON和CSV的表示方式不同,可以通过第一行进行区分
JSON例如可能以{[{开头
XML例如应该以<?xml version="1.0" encoding="UTF-8"?>开头
CSV有点棘手,因为第一行可以有更多种类的输入,但根据预期的数据,您可以自定义其他测试。

这种方法显然不能验证实际数据,但可以帮助检测您正在处理的格式。

最新更新