Как вы динамически идентифицируете неизвестные разделители в файле данных?
У меня есть три файла входных данных. Каждый из них использует другой разделитель для содержащихся в нем данных. Файл данных выглядит следующим образом:
apples | bananas | oranges | grapes
файл данных два выглядит следующим образом:
quarter, dime, nickel, penny
файл данных три выглядит следующим образом:
horse cow pig chicken goat
(изменение количества столбцов также преднамеренно)
Мысль о том, что я должен был подсчитать количество не-альфа-символов, и предположить, что самый высокий счетчик был символом разделителя. Тем не менее, файлы с не-пространственными разделителями также имеют пробелы до и после разделителей, поэтому пробелы выигрывают во всех трех файлах. Здесь мой код:
def count_chars(s):
valid_seps=[' ','|',',',';','\t']
cnt = {}
for c in s:
if c in valid_seps: cnt[c] = cnt.get(c,0) + 1
return cnt
infile = 'pipe.txt' #or 'comma.txt' or 'space.txt'
records = open(infile,'r').read()
print count_chars(records)
Он будет печатать словарь с подсчетом всех допустимых символов. В каждом случае пространство всегда выигрывает, поэтому я не могу полагаться на это, чтобы сообщить мне, что такое разделитель.
Но я не могу придумать лучшего способа сделать это.
Любые предложения?
Ответы
Ответ 1
Если вы используете python, я бы предложил просто вызвать re.split в строке со всеми допустимыми ожидаемыми разделителями:
>>> l = "big long list of space separated words"
>>> re.split(r'[ ,|;"]+', l)
['big', 'long', 'list', 'of', 'space', 'separated', 'words']
Единственная проблема заключается в том, что если один из файлов использовал разделитель как часть данных.
Если вы должны идентифицировать разделитель, лучше всего подсчитать все, кроме пробелов. Если почти нет вхождений, то это, вероятно, пробел, в противном случае это максимальное количество отображаемых символов.
К сожалению, действительно нет способа быть уверенным. У вас могут быть пробельные данные, заполненные запятыми, или у вас может быть | разделенные данные, заполненные точкой с запятой. Это может не всегда работать.
Ответ 2
Как насчет того, чтобы попробовать стандарт Python CSV: http://docs.python.org/library/csv.html#csv.Sniffer
import csv
sniffer = csv.Sniffer()
dialect = sniffer.sniff('quarter, dime, nickel, penny')
print dialect.delimiter
# returns ','
Ответ 3
Я закончил работу с регулярным выражением из-за проблемы пробелов. Здесь мой готовый код, в случае, если кто-то заинтересован, или может использовать что-нибудь еще в нем. По тангенциальной ноте было бы аккуратно найти способ динамически идентифицировать порядок столбцов, но я понимаю, что немного сложнее. Тем временем я отказываюсь от старых трюков, чтобы разобраться с этим.
for infile in glob.glob(os.path.join(self._input_dir, self._file_mask)):
#couldn't quite figure out a way to make this a single block
#(rather than three separate if/elifs. But you can see the split is
#generalized already, so if anyone can come up with a better way,
#I'm all ears!! :)
for row in open(infile,'r').readlines():
if infile.find('comma') > -1:
datefmt = "%m/%d/%Y"
last, first, gender, color, dobraw = \
[x.strip() for x in re.split(r'[ ,|;"\t]+', row)]
elif infile.find('space') > -1:
datefmt = "%m-%d-%Y"
last, first, unused, gender, dobraw, color = \
[x.strip() for x in re.split(r'[ ,|;"\t]+', row)]
elif infile.find('pipe') > -1:
datefmt = "%m-%d-%Y"
last, first, unused, gender, color, dobraw = \
[x.strip() for x in re.split(r'[ ,|;"\t]+', row)]
#There is also a way to do this with csv.Sniffer, but the
#spaces around the pipe delimiter also confuse sniffer, so
#I couldn't use it.
else: raise ValueError(infile + "is not an acceptable input file.")