سؤال

نظرا لسلسلة مثل هذا:

"سلسلة، مع"، "مختلف"، "القيم، وبعض"، مقتبس

ما هي الخوارزمية الجيدة لتقسيم هذا بناءً على الفواصل مع تجاهل الفواصل داخل الأقسام المقتبسة؟

يجب أن يكون الإخراج مصفوفة:

[ "a"، "سلسلة، مع"، "مختلف"، "قيم، وبعض"، "مقتبس" ]

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

المحلول

إذا كانت اللغة التي اخترتها لا تقدم طريقة للقيام بذلك دون تفكير، فسأفكر في البداية في خيارين باعتبارهما الطريق السهل للخروج:

  1. التحليل المسبق واستبدال الفواصل داخل السلسلة بحرف تحكم آخر ثم تقسيمها، متبوعًا بتحليل لاحق على المصفوفة لاستبدال حرف التحكم المستخدم مسبقًا بالفواصل.

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

ومع ذلك، فهذه مجرد حيل، وإذا كان هذا تمرينًا "ذهنيًا" خالصًا، فأنا أظن أنها لن تكون مفيدة.إذا كانت هذه مشكلة حقيقية، فسيكون من المفيد معرفة اللغة حتى نتمكن من تقديم بعض النصائح المحددة.

نصائح أخرى

يبدو أن لديك بعض الإجابات الجيدة هنا.

بالنسبة لأولئك منكم الذين يتطلعون إلى التعامل مع تحليل ملف CSV الخاص بكم، استمعوا إلى نصيحة الخبراء لا تقم بتدوير محلل CSV الخاص بك.

فكرتك الأولى هي "أحتاج إلى التعامل مع الفواصل داخل علامات الاقتباس."

فكرتك القادمة ستكون، "أوه، يا للهول، أحتاج إلى التعامل مع علامات الاقتباس داخل علامات الاقتباس.اقتباسات هاربة.التنصيص.ونقلت واحدة..."

إنه الطريق إلى الجنون.لا تكتب بنفسك.ابحث عن مكتبة تحتوي على تغطية واسعة النطاق لاختبارات الوحدات التي تضرب جميع الأجزاء الصعبة وقد مرت بالجحيم من أجلك.بالنسبة لـ .NET، استخدم الإصدار المجاني FileHelpers مكتبة.

بايثون:

import csv
reader = csv.reader(open("some.csv"))
for row in reader:
    print row

بالطبع يعد استخدام محلل CSV أفضل، ولكن من أجل المتعة فقط، يمكنك:

Loop on the string letter by letter.
    If current_letter == quote : 
        toggle inside_quote variable.
    Else if (current_letter ==comma and not inside_quote) : 
        push current_word into array and clear current_word.
    Else 
        append the current_letter to current_word
When the loop is done push the current_word into array 

قام المؤلف هنا بإسقاط كتلة من كود C# الذي يتعامل مع السيناريو الذي تواجه مشكلة فيه:

استيراد ملف CSV في .Net

لا ينبغي أن يكون من الصعب جدًا ترجمتها.

ماذا لو ظهر عدد فردي من عروض الأسعار في السلسلة الأصلية؟

يبدو هذا بشكل غريب مثل تحليل CSV، الذي يتميز ببعض الخصائص المميزة للتعامل مع الحقول المقتبسة.يتم تهريب الحقل فقط إذا كان الحقل محددًا بعلامات اقتباس مزدوجة، لذلك:

الحقل 1، "الحقل 2، الحقل 3"، الحقل 4، "الحقل 5، الحقل 6" الحقل 7

يصبح

المجال 1

الحقل 2، الحقل 3

المجال4

"الحقل 5

الحقل 6" الحقل 7

لاحظ أنه إذا لم يبدأ وينتهي باقتباس، فهو ليس حقلاً مقتبسًا ويتم التعامل مع علامات الاقتباس المزدوجة ببساطة على أنها علامات اقتباس مزدوجة.

بشكل داخلي، لا يتعامل الكود الخاص بي الذي يرتبط به شخص ما مع هذا بشكل صحيح، إذا كنت أتذكر بشكل صحيح.

فيما يلي تنفيذ بسيط لبايثون يعتمد على الكود الكاذب لبات:

def splitIgnoringSingleQuote(string, split_char, remove_quotes=False):
    string_split = []
    current_word = ""
    inside_quote = False
    for letter in string:
      if letter == "'":
        if not remove_quotes:
           current_word += letter
        if inside_quote:
          inside_quote = False
        else:
          inside_quote = True
      elif letter == split_char and not inside_quote:
        string_split.append(current_word)
        current_word = ""
      else:
        current_word += letter
    string_split.append(current_word)
    return string_split

أستخدم هذا لتحليل السلاسل، ولست متأكدًا مما إذا كان يساعد هنا؛ولكن مع بعض التعديلات الطفيفة ربما؟

function getstringbetween($string, $start, $end){
    $string = " ".$string;
    $ini = strpos($string,$start);
    if ($ini == 0) return "";
    $ini += strlen($start);   
    $len = strpos($string,$end,$ini) - $ini;
    return substr($string,$ini,$len);
}

$fullstring = "this is my [tag]dog[/tag]";
$parsed = getstringbetween($fullstring, "[tag]", "[/tag]");

echo $parsed; // (result = dog) 

/ النائب

هذا تحليل قياسي بنمط CSV.يحاول الكثير من الأشخاص القيام بذلك باستخدام التعبيرات العادية.يمكنك الوصول إلى حوالي 90% باستخدام التعابير المنطقية، لكنك تحتاج حقًا إلى محلل CSV حقيقي للقيام بذلك بشكل صحيح.لقد وجدت ال محلل C# CSV سريع وممتاز على CodeProject قبل بضعة أشهر وأنا أوصي بشدة!

إليك واحدة بالرمز الكاذب (ويعرف أيضًا باسم.Python) في مسار واحد :-P

def parsecsv(instr):
    i = 0
    j = 0

    outstrs = []

    # i is fixed until a match occurs, then it advances
    # up to j. j inches forward each time through:

    while i < len(instr):

        if j < len(instr) and instr[j] == '"':
            # skip the opening quote...
            j += 1
            # then iterate until we find a closing quote.
            while instr[j] != '"':
                j += 1
                if j == len(instr):
                    raise Exception("Unmatched double quote at end of input.")

        if j == len(instr) or instr[j] == ',':
            s = instr[i:j]  # get the substring we've found
            s = s.strip()    # remove extra whitespace

            # remove surrounding quotes if they're there
            if len(s) > 2 and s[0] == '"' and s[-1] == '"':
                s = s[1:-1]

            # add it to the result
            outstrs.append(s)

            # skip over the comma, move i up (to where
            # j will be at the end of the iteration)
            i = j+1

        j = j+1

    return outstrs

def testcase(instr, expected):
    outstr = parsecsv(instr)
    print outstr
    assert expected == outstr

# Doesn't handle things like '1, 2, "a, b, c" d, 2' or
# escaped quotes, but those can be added pretty easily.

testcase('a, b, "1, 2, 3", c', ['a', 'b', '1, 2, 3', 'c'])
testcase('a,b,"1, 2, 3" , c', ['a', 'b', '1, 2, 3', 'c'])

# odd number of quotes gives a "unmatched quote" exception
#testcase('a,b,"1, 2, 3" , "c', ['a', 'b', '1, 2, 3', 'c'])

إليك خوارزمية بسيطة:

  1. تحديد ما إذا كانت السلسلة تبدأ بـ a '"' شخصية
  2. قم بتقسيم السلسلة إلى مصفوفة محددة بواسطة '"' شخصية.
  3. ضع علامة على الفواصل المقتبسة باستخدام عنصر نائب #COMMA#
    • إذا كان الإدخال يبدأ بـ a '"', ، ضع علامة على تلك العناصر في المصفوفة حيث الفهرس % 2 == 0
    • بخلاف ذلك، قم بوضع علامة على تلك العناصر في المصفوفة حيث الفهرس % 2 == 1
  4. قم بتسلسل العناصر الموجودة في المصفوفة لتكوين سلسلة إدخال معدلة.
  5. قم بتقسيم السلسلة إلى مصفوفة محددة بواسطة ',' شخصية.
  6. استبدال كافة المثيلات في مجموعة #COMMA# العناصر النائبة مع ',' شخصية.
  7. المصفوفة هي مخرجاتك

هنا تنفيذ بايثون:
(تم إصلاحه للتعامل مع '"a,b,c,d,e,f,h",i,j,k"')

def parse_input(input):

    quote_mod = int(not input.startswith('"'))

    input = input.split('"')
    for item in input:
        if item == '':
            input.remove(item)
    for i in range(len(input)):
        if i % 2 == quoted_mod:
            input[i] = input[i].replace(",", "#COMMA#")

    input = "".join(input).split(",")
    for item in input:
        if item == '':
            input.remove(item)
    for i in range(len(input)):
        input[i] = input[i].replace("#COMMA#", ",")
    return input

# parse_input('a,"string, with",various,"values, and some",quoted')
#  -> ['a,string', ' with,various,values', ' and some,quoted']
# parse_input('"a,b",c,"d,e,f,h","i,j,k"')
#  -> ['a,b', 'c', 'd,e,f,h', 'i,j,k']

لم أستطع مقاومة معرفة ما إذا كان بإمكاني أن أجعلها تعمل في سطر واحد من لغة بايثون:

arr = [i.replace("|", ",") for i in re.sub('"([^"]*)\,([^"]*)"',"\g<1>|\g<2>", str_to_test).split(",")]

إرجاع ["a"، "سلسلة، مع"، "مختلف"، "قيم، وبعض"، "مقتبس"]

إنه يعمل أولاً باستبدال "، Inside Quotes إلى فاصل آخر (|) ، وتقسيم السلسلة على" ، "واستبدال | فاصل مرة أخرى.

نظرًا لأنك قلت لغة محايدة، فقد كتبت الخوارزمية الخاصة بي باللغة الأقرب إلى الكود الكاذب قدر الإمكان:

def find_character_indices(s, ch):
    return [i for i, ltr in enumerate(s) if ltr == ch]


def split_text_preserving_quotes(content, include_quotes=False):
    quote_indices = find_character_indices(content, '"')

    output = content[:quote_indices[0]].split()

    for i in range(1, len(quote_indices)):
        if i % 2 == 1: # end of quoted sequence
            start = quote_indices[i - 1]
            end = quote_indices[i] + 1
            output.extend([content[start:end]])

        else:
            start = quote_indices[i - 1] + 1
            end = quote_indices[i]
            split_section = content[start:end].split()
            output.extend(split_section)

        output += content[quote_indices[-1] + 1:].split()                                                                 

    return output
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top