簡體   English   中英

在字符串列表中查找分隔符/定界符

[英]Finding separators/delimiters in lists of strings

我試圖在可能包含或不包含分隔符的文件中查找分隔符,並且這些分隔符是什么(如果有的話)也是未知的。

到目前為止,我已經編寫了以下代碼來嘗試“解決”此問題:

strings = [
    'cabhb2k4ack_sfdfd~ffrref_lk',
    'iodja_24ed~092oi3jelk_fcjcad',
    'lkn04432m_90osidjlknxc~o_pf'
]

# Process first line
line1 = strings[0]
separators = set()
for sep in set(line1):
    separators.add(( sep, line1.count(sep) ))

# Process all the others
for line in strings:
    for sep,sepcount in separators.copy():
        if line.count(sep) != sepcount: separators.remove( (sep,sepcount) )

print separators

它返回一個好的set: set([('_', 2), ('~', 1)]) -不幸的是,它不包含分隔符的順序。 實際上,甚至不知道這些分隔符的順序是否一致。

分隔符的規則很簡單:

  1. 每行它們必須發生相同的次數,
  2. 它們必須在每一行上以相同的順序出現,
  3. 非分隔符都不能是分隔符。

請注意,在上面的示例中,“ 4”作為分隔符被排除在外,因為第三個字符串在原因1和3中出現兩次。

問題
如何修改此代碼以檢查規則2正確打印分隔符的順序?

我會用一個Counter來代替.count ,采取skrrgwasme的建議,使用列表,並使用itertools.combinations幫助疊代可能分離的子集:

from collections import Counter
from itertools import combinations

def subsets(elems):
    for width in range(1, len(elems)+1):
        for comb in combinations(elems, width):
            yield comb

def sep_order(string, chars):
    chars = set(chars)
    order = tuple(c for c in string if c in chars)
    return order

def find_viable_separators(strings):
    counts = [Counter(s) for s in strings]
    chars = {c for c in counts[0]
             if all(count[c]==counts[0][c] for count in counts)}
    for seps in subsets(chars):
        orders = {sep_order(s, seps) for s in strings}
        if len(orders) == 1:
            yield seps, next(iter(orders))

這給了我

>>> 
... strings = [
...     'cabhb2k4ack_sfdfd~ffrref_lk',
...     'iodja_24ed~092oi3jelk_fcjcad',
...     'lkn04432m_90osidjlknxc~o_pf'
... ]
... 
... for seps, order in find_viable_separators(strings):
...     print("possible separators:", seps, "with order:", order)
...             
possible separators: ('~',) with order: ('~',)
possible separators: ('_',) with order: ('_', '_')
possible separators: ('~', '_') with order: ('_', '~', '_')

給定規則1,每個分隔符都有從列表的第一行到最后一行穩定的出現次數/行。

我發現規則3的表達不夠好。 我認為必須將其理解為:“在該行中被視為非分隔符的其他字符中找不到所有用作分隔符的字符”。

因此,在給定規則1和3的情況下,每個出現次數/行數都變化的字符即使在連續的兩行之間只有一次也不能成為分隔符。

因此,以下代碼的原理是
·首先創建第一行中存在的所有字符的列表sep_n ,並將其與第一行中的出現次數相關聯,
·然后沿着S行的列表進行迭代,並消除列表sep_n次數不相同的每個字符。

S = [
    'cabhb2k4ack_sfdfd~ffrref_lk',
    'iodja_24ed~092oi3jelk_fcjcad',
    'lkn04432m_90osidjlknxc~o_pf',
    'hgtr5v_8mgojnb5+87rt~lhiuhfj_n547'
    ]
# 1.They must occur the same number of times per line, 
line0 = S.pop(0)
sep_n = [ (c,line0.count(c)) for c in line0]
print(line0); print(sep_n,'\n')

for line in S:
    sep_n = [x for x in sep_n if line.count(x[0]) == x[1]]
    print(line); print(sep_n,'\n')

S.insert(0, line0)

# 2.They must occur in the same order on each line,
separators_in_order = [x[0] for x in sep_n]
print('separators_in_order : ',separators_in_order)
separators          = ''.join(set(separators_in_order))

for i,line in enumerate(S):
    if [c for c in line if c in separators] != separators_in_order:
        print(i,line)

如果各行中的字符有足夠的變化形式(分隔符除外),則我的代碼中sep_n長度會隨着列表的迭代而迅速減少。

指令sep_n = [ (c,line0.count(c)) for c in line0]負責以下事實:在separators_in_order獲得的最終順序是列表S的第一行中的順序。

但是我無法想象一種方法來測試分隔符從一行到另一行的順序是否相同。 實際上,在我看來,不可能在迭代期間進行這樣的測試,因為只有在完全執行了迭代之后才完全知道分隔符列表。

這就是為什么在獲取sep_n的值之后必須執行輔助控件的原因。 它需要再次遍歷列表S
問題是,如果“出現次數/行數變化的每個字符甚至在連續的兩行之間只有一次不能改變 ”,則可能會出現非分隔符字符的出現次數完全相同的情況。在所有行中都存在“非分隔符”,因此就不可能根據出現的次數將其檢測為非分隔符。
但是由於這樣的非分隔字符仍然不會總是出現在穩定出現的字符列表中的同一位置,因此有可能進行二次驗證。

最后,可能存在的一種極端情況是:非分隔符出現在所有行中,它們的出現完全相同,並且放置在行中的分隔符之間,這樣即使通過二次驗證也無法檢測到;
我不知道該怎么解決...

結果是

cabhb2k4ack_sfdfd~ffrref_lk
[('c', 2), ('a', 2), ('b', 2), ('h', 1), ('b', 2), ('2', 1), ('k', 3), ('4', 1), ('a', 2), ('c', 2), ('k', 3), ('_', 2), ('s', 1), ('f', 5), ('d', 2), ('f', 5), ('d', 2), ('~', 1), ('f', 5), ('f', 5), ('r', 2), ('r', 2), ('e', 1), ('f', 5), ('_', 2), ('l', 1), ('k', 3)] 

iodja_24ed~092oi3jelk_fcjcad
[('c', 2), ('a', 2), ('4', 1), ('a', 2), ('c', 2), ('_', 2), ('~', 1), ('_', 2), ('l', 1)] 

lkn04432m_90osidjlknxc~o_pf
[('_', 2), ('~', 1), ('_', 2)] 

hgtr5v_8mgojnb5+87rt~lhiuhfj_n547
[('_', 2), ('~', 1), ('_', 2)] 

separators_in_order :  ['_', '~', '_']

S = [
    'cabhb2k4ack_sfd#fd~ffrref_lk',
    'iodja_24ed~092oi#3jelk_fcjcad',
    'lkn04432m_90osi#djlknxc~o_pf',
    'h#gtr5v_8mgojnb5+87rt~lhiuhfj_n547'
    ]

結果是

cabhb2k4ack_sfd#fd~ffrref_lk
[('c', 2), ('a', 2), ('b', 2), ('h', 1), ('b', 2), ('2', 1), ('k', 3), ('4', 1), ('a', 2), ('c', 2), ('k', 3), ('_', 2), ('s', 1), ('f', 5), ('d', 2), ('#', 1), ('f', 5), ('d', 2), ('~', 1), ('f', 5), ('f', 5), ('r', 2), ('r', 2), ('e', 1), ('f', 5), ('_', 2), ('l', 1), ('k', 3)] 

iodja_24ed~092oi#3jelk_fcjcad
[('c', 2), ('a', 2), ('4', 1), ('a', 2), ('c', 2), ('_', 2), ('#', 1), ('~', 1), ('_', 2), ('l', 1)] 

lkn04432m_90osi#djlknxc~o_pf
[('_', 2), ('#', 1), ('~', 1), ('_', 2)] 

h#gtr5v_8mgojnb5+87rt~lhiuhfj_n547
[('_', 2), ('#', 1), ('~', 1), ('_', 2)] 

separators_in_order :  ['_', '#', '~', '_']
1 iodja_24ed~092oi#3jelk_fcjcad
3 h#gtr5v_8mgojnb5+87rt~lhiuhfj_n547


NB 1
指令line0 = S.pop(0)完成
for line in S[1:]:指令for line in S[1:]:關閉,
因為S[1:]創建了一個新列表,可能很重。

注意2
為了避免在S中每次迭代時都創建新的sep_n列表,
最好將迭代編寫如下:

for line in S:
    for x in sep_n:
        if line.count(x[0]) == x[1]:
            sep_n = [x for x in sep_n if line.count(x[0]) == x[1]]
            break
    print(line); print(sep_n,'\n')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM