تقسيم سلسلة بواسطة محدد بطريقة حساسة للسياق

StackOverflow https://stackoverflow.com/questions/1019756

  •  06-07-2019
  •  | 
  •  

سؤال

على سبيل المثال، أريد الانقسام

str = '"a,b,c",d,e,f'

داخل

["a,b,c",'d','e','f']

(أي.لا تقم بتقسيم الجزء المقتبس) في هذه الحالة، يمكن القيام بذلك باستخدام

re.findall('".*?"|[^,]+',str)

ومع ذلك، إذا

str = '"a,,b,c",d,,f'

أريد

["a,,b,c",'d','','f']

أي.أريد سلوكًا يشبه وظيفة الانقسام في بايثون.هل هناك أي طريقة يمكنني من خلالها القيام بذلك في سطر واحد (صغير)، ربما باستخدام مكتبة إعادة بايثون؟

في الواقع، لقد أدركت للتو (على هذا الموقع) أن وحدة CSV مثالية لما أريد القيام به، ولكن لدي فضول لمعرفة ما إذا كان هناك تعبير عادي يمكن استخدامه للقيام بذلك أيضًا.

هل كانت مفيدة؟

المحلول

re.split(',(?=(?:[^"]*"[^"]*")*[^"]*$)', str)

وبعد مطابقة فاصلة، إذا كان هناك عدد فردي من الاقتباس يمثل حتى قدما إلى الأمام، يجب أن تكون فاصلة داخل زوج من علامات الاقتباس، لذلك لا يعتبر محدد. ومن الواضح أن هذا لا يأخذ إمكانية علامات الاقتباس هرب في الاعتبار، ولكن يمكن التعامل معها إذا لزم الأمر - انها مجرد يجعل التعبير المعتاد حول ضعف قبيحة كما هي عليه بالفعل. : D

نصائح أخرى

استخدم وحدة CSV كما هو محلل الحقيقي. التعابير العادية هي nonoptimal (أو غير ملائمة تماما) عن معظم الأشياء التي تنطوي على محددات مطابقة التي تتغير قواعد (أنا غير متأكد ما إذا كان هذا هو قواعد معينة منتظمة أم لا). كنت قد تكون قادرة على خلق التعابير المنطقية التي من شأنها العمل في هذه الحالة، ولكن سيكون معقدة نوعا ما (وخاصة التعامل مع قضايا مثل "وقال: \" كيف حالك \ "").

وكتابة آلة الدولة لهذا من شأنه، من ناحية أخرى، يبدو أن تكون واضحة تماما. DFAS وregexes لها نفس القوة، ولكن عادة واحد منهم هو أكثر ملاءمة لهذه المشكلة في متناول اليد، وعادة ما تعتمد اعتمادا كبيرا على منطق إضافية قد تحتاج إلى تنفيذ.

<وأ href = "http://books.google.com/books؟id=GX3w_18-JegC&lpg=PA401&ots=PHojQnfpRY&dq=comma-separated٪20٪22mastering٪20regular٪20expressions٪22&pg=PA271" يختلط = "نوفولو noreferrer "> صفحة 271 من لفريدل <م> اتقان التعبير العادية لديه تعبير عادي لاستخراج حقول CSV ربما نقلت، ولكنه يتطلب قليلا من تحليل نتائج العمل:

>>> re.findall('(?:^|,)(?:"((?:[^"]|"")*)"|([^",]*))',str)
[('a,b,c', ''), ('', 'd'), ('', 'e'), ('', 'f')]
>>> re.findall('(?:^|,)(?:"((?:[^"]|"")*)"|([^",]*))','"a,b,c",d,,f')
[('a,b,c', ''), ('', 'd'), ('', ''), ('', 'f')]

ونفس النمط مع العلم مطول:

csv = re.compile(r"""
    (?:^|,)
    (?: # now match either a double-quoted field
        # (inside, paired double quotes are allowed)...
        " # (double-quoted field's opening quote)
          (    (?: [^"] | "" )*    )
        " # (double-quoted field's closing quote)
    |
      # ...or some non-quote/non-comma text...
        ( [^",]* )
    )""", re.X)

ويمكنك الحصول على وثيقة باستخدام المحددات غير الجشع. الأقرب عندي هو:

>>> re.findall('(".*?"|.*?)(?:,|$)',  '"a,b,c",d,e,f')
['"a,,b,c"', 'd', '', 'f', '']

ولكن كما ترون، كنت في نهاية المطاف مع سلسلة فارغة زائدة في النهاية، الذي لا يمكن تمييزه من النتيجة التي تحصل عندما تنتهي السلسلة مع فاصلة:

>>> re.findall('(".*?"|.*?)(?:,|$)', '"a,b,c",d,e,f,')
['"a,,b,c"', 'd', '', 'f', '']

لذلك كنت بحاجة إلى القيام ببعض التغيير والتبديل اليدوي في النهاية - شيء من هذا القبيل:

matches = regex,findall(s)
if not s.endswith(","): matches.pop()

أو

matches = regex.findall(s+",")[:-1]

وهناك ربما بطريقة أفضل.

إليك الوظيفة التي ستنجز المهمة:

def smart_split(data, delimiter=","):
    """ Performs splitting with string preservation. This reads both single and
        double quoted strings.
    """
    result = []
    quote_type = None
    buffer = ""
    position = 0
    while position < len(data):
        if data[position] in ["\"", "'"]:
            quote_type = data[position]
            while quote_type is not None:
                position += 1
                if data[position] == quote_type:
                    quote_type = None
                    position += 1
                else:
                    buffer += data[position]
        if data[position] == delimiter:
            result.append(buffer)
            buffer = ""
        else:
            buffer += data[position]
        position += 1
    result.append(buffer)
    return result

مثال للاستخدام:

str = '"a,b,c",d,e,f'
print smart_split(str)
# Prints: ['a,b,c', 'd', 'e', 'f']

وهنا وظيفة قصيرة حقا من شأنها أن تفعل الشيء نفسه:

def split (aString):
    splitByQuotes = (",%s,"%aString).split('"')
    splitByQuotes[0::2] = [x.split(",")[1:-1] for x in splitByQuotes[0::2]]
    return [a.strip() \
        for b in splitByQuotes \
        for a in (b if type(b)==list else [b])]

ووالانقسام السلسلة حيث هي ونقلت وإنشاء قائمة حيث كل عنصر حتى هي الاشياء خارج ونقلت وكل عنصر غريب هي الاشياء التي كانت مغلفة ضمن علامات اقتباس. الاشياء في الاقتباس فإنه يترك وحده، والاشياء خارجها انشقاقات حيث الفواصل. الآن لدينا قائمة من القوائم بالتناوب والسلاسل، ونحن ثم بسط مع السطر الأخير. سبب التفاف السلسلة في الفواصل في بداية وإزالة الفواصل في منتصف هو لمنع العناصر الفارغة الغيار في القائمة. يجب أن تكون قادرة على التعامل بيضاء - أنا وأضاف وظيفة قطاع () في نهاية لجعله إنتاج إخراج نظيفة، ولكن هذا ليس من الضروري

والاستعمال:

>>> print split('c, , "a,,b,c",d,"moo","f"')
['c', '', 'a,,b,c', 'd', 'moo', 'f']
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top