25

3つの入力データファイルがあります。それぞれが、そこに含まれるデータに対して異なる区切り文字を使用します。データファイル1は次のようになります。

りんご| バナナ| オレンジ| ブドウ

データファイル2は次のようになります。

クォーター、ダイム、ニッケル、ペニー

データファイル3は次のようになります。

馬牛豚鶏山羊

(列数の変更も意図的なものです)

私が考えたのは、非英字の数を数えることであり、最も多い数は区切り文字であると推測しました。ただし、スペース以外の区切り文字が含まれるファイルには、区切り文字の前後にもスペースがあるため、3つのファイルすべてでスペースが優先されます。これが私のコードです:

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)

受け入れ可能なすべての文字の数を含む辞書を印刷します。いずれの場合も、スペースが常に勝つため、セパレーターが何であるかを教えてくれることに頼ることはできません。

しかし、私はこれを行うためのより良い方法を考えることはできません。

助言がありますか?

4

4 に答える 4

96

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 ','
于 2010-10-17T05:53:00.673 に答える
5

Pythonを使用している場合は、予想されるすべての有効な区切り文字を使用して、行でre.splitを呼び出すことをお勧めします。

>>> l = "big long list of space separated words"
>>> re.split(r'[ ,|;"]+', l)
['big', 'long', 'list', 'of', 'space', 'separated', 'words']

唯一の問題は、ファイルの1つがデータの一部としてセパレーターを使用した場合です。

セパレータを特定する必要がある場合は、スペースを除くすべてを数えるのが最善の策です。オカレンスがほとんどない場合は、おそらくスペースです。それ以外の場合は、マップされた文字の最大値です。

残念ながら、確実にする方法は本当にありません。スペースで区切られたデータがコンマで埋められている場合もあれば、|がある場合もあります。セミコロンで満たされた区切られたデータ。常に機能するとは限りません。

于 2010-10-17T05:24:17.713 に答える
1

スペースの問題のため、正規表現を使用することになりました。誰かが興味を持っているか、他の何かを使用できる場合に備えて、これが私の完成したコードです。接線方向のメモでは、列の順序を動的に識別する方法を見つけるのは良いことですが、それはもう少し注意が必要です。その間、私はそれを整理するために古いトリックに頼っています。

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

于 2010-10-18T15:08:19.413 に答える
0

ほとんどの場合、いくつかの事前情報(一般的な区切り文字のリストなど)と、すべての行が同じ数の区切り文字を与える頻度カウントに基づいて、区切り文字を正しく決定できます。

def head(filename: str, n: int):
    try:
        with open(filename) as f:
            head_lines = [next(f).rstrip() for x in range(n)]
    except StopIteration:
        with open(filename) as f:
            head_lines = f.read().splitlines()
    return head_lines


def detect_delimiter(filename: str, n=2):
    sample_lines = head(filename, n)
    common_delimiters= [',',';','\t',' ','|',':']
    for d in common_delimiters:
        ref = sample_lines[0].count(d)
        if ref > 0:
            if all([ ref == sample_lines[i].count(d) for i in range(1,n)]):
                return d
    return ','

多くの場合、n = 2行で十分です。より堅牢な回答が得られるように、より多くの行を確認してください。もちろん、誤検出につながる場合(多くの場合、人工的なもの)がありますが、実際には起こりそうにありません。

ここでは、ファイルのn番目の最初の行のみを読み取るhead関数の効率的なPython実装を使用しています。ファイルの最初のN行を読み取る方法に関する私の回答を参照してください

于 2021-07-08T22:38:26.943 に答える