2009-09-07 6 views
0

Я ожидаю пользователей, чтобы загрузить CSV-файл максимального размера 1Мб на веб-форму, которая должна соответствовать данный формат, аналогичный:Проверка CSV против данного формата

"<String>","<String>",<Int>,<Float> 

Это будет обработан позднее. Я хотел бы подтвердить, что файл соответствует указанному формату, так что программа, которая позже будет использовать файл, не получит неожиданного ввода и что нет проблем с безопасностью (скажем, инъекция для скрипта синтаксического анализа, которая выполняет некоторые вычисления и вставка db).

(1) Что было бы самым лучшим способом сделать это, чтобы быть быстрым и тщательным? Из того, что я исследовал, я мог пойти по пути регулярного выражения или чего-то большего, как this. Я просмотрел модуль csv python, но, похоже, он не имеет встроенной проверки.

(2) Предполагаю, что я иду на регулярное выражение, может ли кто-нибудь направить меня к лучшему способу сделать это? Согласны ли я с незаконными символами и отказываюсь от этого? (например, no '/' '\' '<' '>' '{' '}' и т. д.) или соответствовать всем юридическим, например. [a-zA-Z0-9] {1,10} для строкового компонента? Я не слишком хорошо знаком с регулярными выражениями, поэтому оценщики и примеры будут оценены.

EDIT: Строки не должны содержать запятых или кавычек, в которых просто будет указано имя (то есть имя, фамилия). И да, я забыл добавить, что они будут двойными.

EDIT # 2: Спасибо за все ответы. Cutplace довольно интересна, но является автономной. В конце концов решила пойти с pyparsing, потому что это дает большую гибкость, если я добавлю больше форматов.

+0

что бы ваш формат сделать для строк, которые содержат запятые (строка должна быть в двойных кавычках) – moogs

ответ

3

Pyparsing будет обрабатывать эти данные и будет терпимым к неожиданным вещам, например, пробелам до запятых и запятыми, запятыми в кавычках и т. Д. (Csv-модуль тоже, но регулярные решения заставляют вас добавлять «\ s *» биты все над местом).

from pyparsing import * 

integer = Regex(r"-?\d+").setName("integer") 
integer.setParseAction(lambda tokens: int(tokens[0])) 
floatnum = Regex(r"-?\d+\.\d*").setName("float") 
floatnum.setParseAction(lambda tokens: float(tokens[0])) 
dblQuotedString.setParseAction(removeQuotes) 
COMMA = Suppress(',') 
validLine = dblQuotedString + COMMA + dblQuotedString + COMMA + \ 
     integer + COMMA + floatnum + LineEnd() 

tests = """\ 
"good data","good2",100,3.14 
"good data" , "good2", 100, 3.14 
bad, "good","good2",100,3.14 
"bad","good2",100,3 
"bad","good2",100.5,3 
""".splitlines() 

for t in tests: 
    print t 
    try: 
     print validLine.parseString(t).asList() 
    except ParseException, pe: 
     print pe.markInputline('?') 
     print pe.msg 
    print 

Печать

"good data","good2",100,3.14 
['good data', 'good2', 100, 3.1400000000000001] 

"good data" , "good2", 100, 3.14 
['good data', 'good2', 100, 3.1400000000000001] 

bad, "good","good2",100,3.14 
?bad, "good","good2",100,3.14 
Expected string enclosed in double quotes 

"bad","good2",100,3 
"bad","good2",100,?3 
Expected float 

"bad","good2",100.5,3 
"bad","good2",100?.5,3 
Expected "," 

Вы, вероятно, будете зачистка тех кавычек на каком-то будущем, Pyparsing может сделать это во время синтаксического анализа, добавляя:

dblQuotedString.setParseAction(removeQuotes) 

Если вы хотите добавьте поддержку комментариев в свой файл ввода, скажем, «#», за которым следует остальная часть строки, вы можете сделать это:

comment = '#' + restOfline 
validLine.ignore(comment) 

Вы также можете добавить имена этих полей, так что вы можете получить доступ к ним по имени, а не позиции индекса (который я нахожу дает более надежный код в свете изменений вниз по дороге):

validLine = dblQuotedString("key") + COMMA + dblQuotedString("title") + COMMA + \ 
     integer("qty") + COMMA + floatnum("price") + LineEnd() 

И ваш код после обработки может затем сделать это:

data = validLine.parseString(t) 
print "%(key)s: %(title)s, %(qty)d in stock at $%(price).2f" % data 
print data.qty*data.price 
0

В идеале, вы хотите, чтобы ваша фильтрация была как можно более ограничительной - чем меньше вещей вы позволяете, тем меньше потенциальных возможностей атаки. Например, поле float или int имеет очень малое количество символов (и очень мало конфигураций этих символов), которые фактически должны быть разрешены. Фильтрация строк в идеале должна быть ограничена только тем, что у людей у ​​людей есть причина для ввода - не зная более обширного контекста, трудно сказать вам точно, что вы должны разрешить, но на минимальном уровне регулярное выражение в строке должно требовать цитирования строк и запрета все, что рано или поздно закончило бы строку.

Помните, однако, что некоторые имена могут содержать такие вещи, как одиночные кавычки (например, «O'Neil») или тире, поэтому вы не можете их исключить.

Что-то вроде ...

/"[a-zA-Z' -]+"/ 

..., вероятно, будет идеальным для строк в двойных кавычках, которые, как предполагается, содержат имена. Вы можете заменить + на длину {x,y} мин/макс, если вы хотите также использовать определенные длины.

+0

Это не будет работать для людей с не-ASCII символами в именах , как мой друг Зоэ. –

+0

Правильно, поэтому вы должны очень тщательно учитывать, какие символы вы разрешаете на основе ваших целевых данных. – Amber

+0

@Dav - справедливо :-) –

2

Я проголосовал за разбор файла, проверив, что у вас есть 4 компонента на запись, что первые два компонента являются строками, третий - это int (проверка для условий NaN), а четвертый - поплавок (также проверяя условия NaN).

Python будет отличным инструментом для работы.

Я не знаю о каких-либо библиотеках в Python, чтобы иметь дело с проверкой файлов CSV против спецификации, но это действительно не должно быть слишком сложно писать.

import csv 
import math 

dataChecker = csv.reader(open('data.csv')) 
for row in dataChecker: 
    if len(row) != 4: 
     print 'Invalid row length.' 
     return 

    my_int = int(row[2]) 
    my_float = float(row[3]) 

    if math.isnan(my_int): 
     print 'Bad int found' 
     return 

    if math.isnan(my_float): 
     print 'Bad float found' 
     return 

print 'All good!' 
+0

Лучше кода, чем мой! +1 от меня – Santi

1

Вот небольшой отрывок я сделал:

import csv 

f = csv.reader(open("test.csv")) 

for value in f: 
    value[0] = str(value[0]) 
    value[1] = str(value[1]) 
    value[2] = int(value[2]) 
    value[3] = float(value[3]) 

Если вы бежите, что с файлом, который не имеет формат указанный Вами, вы получите исключение:

$ python valid.py 
Traceback (most recent call last): 
    File "valid.py", line 8, in <module> 
    i[2] = int(i[2]) 
ValueError: invalid literal for int() with base 10: 'a3' 

Затем вы можете сделать попытку, за исключением ValueError, чтобы поймать ее и сообщить пользователям, что они сделали неправильно.

1

Для разбора CSV может быть много угловых шкафов, поэтому вы, вероятно, не захотите делать это «вручную». По крайней мере, начните с встроенного пакета/библиотеки на язык, который вы используете, даже если он не выполняет всю «проверку», о которой вы можете думать.

Как только вы доберетесь туда, изучите поля для своего списка «незаконных» символов или просмотрите значения в каждом поле, чтобы определить, что они действительны (если вы можете это сделать). Вам также не требуется регулярное выражение для этой задачи, но это может быть более кратким.

Вы также можете запретить встроенные \ r или \ n, \ 0 или \ t. Просто пройдите по полям и проверьте их после загрузки данных с помощью csv lib.

1

Пробег: Cutplace. Он проверяет, что данные табуляции соответствуют документу управления интерфейсом .

Смежные вопросы