Как вы динамически идентифицируете неизвестные разделители в файле данных?

У меня есть три файла входных данных. Каждый из них использует другой разделитель для содержащихся в нем данных. Файл данных выглядит следующим образом:

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.")