我有一个包含学生信息的CSV(逗号分隔值)文件。列标题看起来像StudentId,StudentFirstName,StudentLastName,StudentZipCode,StudentHeight,StudentCommuteMethod等,后续行包含各个学生的信息。现在,我想编写一个python 2.5脚本,它将过滤条件作为命令行参数,并返回与此过滤条件匹配的学生(行)集。例如,过滤条件可能如下所示(使用伪代码格式):
"StudentCommuteMethod = Bus AND StudentZipCode = 12345"
并且可以调用python脚本:
MyPythonScript.py -filter "<above string>" -i input.csv
这应该返回居住在邮政编码为12345且乘坐公共汽车的区域内的所有学生(行)的列表。过滤器也可以是任意复杂的,并且可以包括任意数量的AND,OR运算符。
问题:
此程序可以让用户指定过滤条件的最佳格式是什么(作为命令行参数)。对于简单表达式,格式应该很简单,并且必须足够强大以表达所有类型的条件。
我希望有一个用于以可视方式表达过滤条件的UI。也许是允许每行输入一个简单的双操作数条件的东西,以及使用AND和OR组合它们的一些有用的方法。它应该能够以上面(1)所确定的格式发出过滤器表达式。是否有一些我可以重用的开源项目?
如果您认为有更好的方法来解决此问题而不是传递命令行表达式+ UI,请随意提及它。最后,用户(对编程不太了解的电气工程师)应该能够轻松输入过滤器表达式。
谢谢!
注意:我无法控制输入或输出格式(两个csv文件)。
答案 0 :(得分:1)
你肯定试图在Python中重新实现SQL。我相信最好使用关系数据库并运行SQL查询。
但是,关于问题1,您可以轻松地让用户在每行数据上输入Python表达式并eval()
。
这是一个工作示例,它使用exec
将列值绑定到局部变量(一个讨厌的黑客,我承认)。为简洁起见,省略了CVS解析。
import optparse, sys
# Assume your CSV data is read into a list of dictionaries
sheet = [
{'StudentId': 1, 'StudentFirstName': 'John', 'StudentLastName': 'Doe', 'StudentZipCode': '12345', 'StudentCommuteMethod': 'Bus'},
{'StudentId': 2, 'StudentFirstName': 'Bob', 'StudentLastName': 'Chen', 'StudentZipCode': '12345', 'StudentCommuteMethod': 'Bus'},
{'StudentId': 3, 'StudentFirstName': 'Jane', 'StudentLastName': 'Smith', 'StudentZipCode': '12345', 'StudentCommuteMethod': 'Train'},
{'StudentId': 4, 'StudentFirstName': 'Dave', 'StudentLastName': 'Burns', 'StudentZipCode': '45467', 'StudentCommuteMethod': 'Bus'},
]
# Options parsing
parser = optparse.OptionParser()
parser.add_option('--filter', type='string', dest='filter')
options, args = parser.parse_args()
# Filter option is required
if options.filter is None:
print >> sys.stderr, 'error: no filter expression given'
sys.exit(1)
# Process rows and build result set
result = []
for row in sheet:
# Bind each column to a local variable (StudentId, StudentFirstName, etc.);
# this allows evaluating Python expressions on a row, for example:
# 'StudentCommuteMethod = "Bus" and StudentZipCode = "12345"'
for col, val in row.iteritems():
exec '%s = %s' % (col, repr(val))
# Apply filter to the row
if eval(options.filter):
result.append(row)
# Print out result set
for row in result:
print row
我使用以下过滤器表达式对其进行了测试:
./MyPythonScript.py --filter 'StudentCommuteMethod == "Bus" and StudentZipCode == "12345"'
./MyPythonScript.py --filter 'StudentCommuteMethod == "Bus" or StudentZipCode == "12345"'
(从命令行运行程序时要注意shell引用规则。)
答案 1 :(得分:1)
这是Danilo的建议略有不同。您可以通过将本地字典传递给exec
来避免eval
为每行绑定变量,并且csv.DictReader返回的dicts可以很好地用于此:
import csv, optparse
infile = open('datafile.csv')
reader = csv.DictReader(infile)
parser = optparse.OptionParser()
parser.add_option('--filter', type='string', dest='filter')
options, args = parser.parse_args()
for row in reader:
if eval(options.filter, row):
print row
这假定输入文件的第一行包含列标题,并且要在表达式中使用的任何标题都必须是有效的Python标识符。