Împărțiți un șir ignorând secțiunile cotate

Cu un șir ca acesta:

a, "șir, cu", diverse, "valori și unele", cotate

Ce este un algoritm bun de a împărți acest lucru pe virgule în timp ce ignorați virgulele din secțiunile citate?

Ieșirea ar trebui să fie o matrice:

["a", "șir, cu", "diverse", "valori și unele", "cotate"]

0
fr hi bn
Ce se întâmplă dacă un șir de citate apare în șirul original?
adăugat autor Brock D, sursa
Acest lucru ar implica un șir de intrare necorespunzător citat, așadar aruncarea unei excepții ar fi o opțiune.
adăugat autor J c, sursa

12 răspunsuri

Python:

import csv
reader = csv.reader(open("some.csv"))
for row in reader:
    print row
0
adăugat
Consider că acest lucru este cel mai bun răspuns. Este exact ceea ce am nevoie!
adăugat autor Alex. S., sursa

Ce se întâmplă dacă apar un număr ciudat de citate   în șirul original?

Acest lucru pare neclintit ca parsarea CSV, care are unele particularități în manipularea câmpurilor cotate. Câmpul este scos doar dacă câmpul este delimitat cu citate dublă, astfel:

câmpul1, "câmpul2, câmpul3", câmpul4, câmpul5, câmpul6 "câmpul7

devine

field1

     

câmpul2, câmpul3

     

field4

     

"field5

     

câmpul6 "câmpul7

Observați dacă nu începe și nu se termină cu un citat, atunci nu este un câmp citat, iar ghilimele duble sunt pur și simplu tratate ca ghilimele duble.

Cu siguranță, codul meu la care cineva este conectat nu face față corect, dacă îmi amintesc corect.

0
adăugat

Desigur, folosind un parser CSV este mai bine, dar doar pentru distracție de ea ai putea:

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 
0
adăugat

Iată un simplu algoritm:

  1. Determine if the string begins with a '"' character
  2. Split the string into an array delimited by the '"' character.
  3. Mark the quoted commas with a placeholder #COMMA#
    • If the input starts with a '"', mark those items in the array where the index % 2 == 0
    • Otherwise mark those items in the array where the index % 2 == 1
  4. Concatenate the items in the array to form a modified input string.
  5. Split the string into an array delimited by the ',' character.
  6. Replace all instances in the array of #COMMA# placeholders with the ',' character.
  7. The array is your output.

Heres implementarea python:
(fixat pentru a manipula "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']
0
adăugat

Folosesc acest lucru pentru a analiza șiruri de caractere, nu sunt sigur dacă ajută aici; dar cu unele modificări minore, poate?

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) 

/ mp

0
adăugat

Nu puteam rezista să văd dacă aș putea să funcționeze într-un Python unic:

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

Returnează ['a', 'șir, cu', 'diverse', 'valori și unele', 'cotate']

Funcționează mai întâi înlocuind cotații "," în interiorul unui alt separator (|), împărțind șirul "," și înlocuind | | separator din nou.

0
adăugat
De unde știi că nu există în șirul original? Ce se evadezi citate in siruri de caractere citate?
adăugat autor MarkJ, sursa

Acesta este un paragraf standard în format CSV. O mulțime de oameni încearcă să facă acest lucru cu expresii regulate. Puteți obține aproximativ 90% cu regexuri, dar într-adevăr aveți nevoie de un parser CSV real pentru a face acest lucru în mod corespunzător. Am găsit un parser rapid, excelent C# CSV pe CodeProject acum câteva luni că Recomand!

0
adăugat
Mulțumiri! Ca tip C#, întotdeauna uit că există o mulțime de biblioteci VB utile acolo pe care le pot folosi. Sincer, cred că sunt prost numiți, deoarece nu sunt cu adevărat VB. Ei sunt doar .NET.
adăugat autor Simon Gillbee, sursa
Există, de asemenea, unul în cadrul .NET, desigur. Chiar dacă este în Microsoft.VisualBasic puteți să-l utilizați încă din C #. msdn.microsoft.com/en-us/library/ & hellip;
adăugat autor MarkJ, sursa

Dacă limba mea de alegere nu a oferit o modalitate de a face acest lucru fără să mă gândesc atunci aș considera inițial două opțiuni ca fiind calea ușoară:

  1. Pre-parsează și înlocuiți virgulele din cadrul șirului cu un alt caracter de control, apoi împărțiți-le, urmat de un post-pars pe matrice pentru a înlocui caracterul de control folosit anterior cu virgule.

    li>
  2. Alternativ, împărțiți-le pe virgule, apoi parcurgeți ulterior matricea rezultată într-o altă matrice de verificare pentru ghilimele principale pe fiecare intrare de matrice și concatenarea intrărilor până când am ajuns la un citat terminativ.

Acestea sunt hack-uri cu toate acestea, și dacă acesta este un exercițiu pur "mental", atunci bănuiesc că se vor dovedi nefolositori. Dacă aceasta este o problemă reală a lumii, atunci aceasta ar ajuta la cunoașterea limbii, astfel încât să putem oferi anumite sfaturi specifice.

0
adăugat
Caut un algoritm pentru o problemă similară în care trebuie să procesez fișiere text uriașe (în GB). Aceste fișiere text conțin date calificate, adică separatorul de câmp / de înregistrare face parte din date atunci când este inclus într-o singură / dublă cotație. Caut un algoritm care să mă ajute să procesez aceste fișiere în paralel (prin fire multiple). Limba pe care o folosim este Java. Dați-mi voie să știu dacă aveți sugestii
adăugat autor Andy Dufresne, sursa

Autorul a coborât într-o pată de cod C# care gestionează scenariul cu care aveți o problemă cu:

Importarea fișierelor CSV în .Net

Nu trebuie să fie prea greu de tradus.

0
adăugat

Se pare că ai niște răspunsuri bune aici.

Pentru cei care doresc să se ocupe de parsarea propriului fișier CSV, țineți cont de sfaturile experților și Nu derulați propriul CSV parser .

Your first thought is, "I need to handle commas inside of quotes."

Your next thought will be, "Oh, crap, I need to handle quotes inside of quotes. Escaped quotes. Double quotes. Single quotes..."

Este un drum spre nebunie. Nu scrieți-vă propriul. Găsiți o bibliotecă cu o acoperire extensivă a unității de testare care lovește toate componentele grele și a trecut prin iad pentru voi. Pentru .NET, utilizați biblioteca gratuită FileHelpers .

0
adăugat
o legătură excelentă pe secretgeek - foarte amuzant. dar răspunde doar la întrebarea celor care folosesc .NET din păcate.
adăugat autor Magnus Smith, sursa
Adevărat; deși sfatul este valabil pentru distribuitorii de pretutindeni: nu jucați rolul propriului parser CSV. ruby are un built-in, și există biblioteci acolo pentru Python, C ++, majoritatea limbilor utilizate pe scară largă.
adăugat autor Judah Himango, sursa
+10 dacă mi-ar lăsa :)
adăugat autor MarkJ, sursa
Și, deși SecretGeek nu pare să știe, există și unul din VB.NET. msdn.microsoft.com/en-us/library/ & hellip;
adăugat autor MarkJ, sursa

Iată unul în pseudocod (a.k.a. Python) într-o singură trecere :-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'])
0
adăugat

Iată o implementare simplă a Python bazată pe pseudocodul lui Pat:

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
0
adăugat