Frage

Gegeben sei eine Zeichenfolge wie diese:

a,"string, with",various,"values, and some",quoted

Was ist ein guter Algorithmus, um dies anhand von Kommas aufzuteilen und dabei die Kommas in den zitierten Abschnitten zu ignorieren?

Die Ausgabe sollte ein Array sein:

[ „a“, „string, with“, „various“, „values, and some“, „quoted“ ]

War es hilfreich?

Lösung

Wenn die Sprache meiner Wahl keine Möglichkeit bieten würde, dies ohne nachzudenken zu tun, würde ich zunächst zwei Optionen als einfachen Ausweg in Betracht ziehen:

  1. Analysieren Sie die Kommas innerhalb der Zeichenfolge vorab und ersetzen Sie sie durch ein anderes Steuerzeichen. Teilen Sie sie dann auf, gefolgt von einer Nachanalyse des Arrays, um das zuvor verwendete Steuerzeichen durch die Kommas zu ersetzen.

  2. Alternativ können Sie sie durch Kommas aufteilen und dann das resultierende Array nachträglich in ein anderes Array analysieren, bei jedem Array-Eintrag auf führende Anführungszeichen prüfen und die Einträge verketten, bis ich ein abschließendes Anführungszeichen erreicht habe.

Dabei handelt es sich jedoch um Hacks, und wenn es sich dabei um eine rein „mentale“ Übung handelt, vermute ich, dass sie sich als nicht hilfreich erweisen werden.Wenn es sich um ein reales Problem handelt, wäre es hilfreich, die Sprache zu kennen, damit wir spezifische Ratschläge geben können.

Andere Tipps

Sieht aus, als hätten Sie hier einige gute Antworten.

Für diejenigen unter Ihnen, die das Parsen Ihrer CSV-Datei selbst übernehmen möchten, befolgen Sie die Ratschläge der Experten und Erstellen Sie nicht Ihren eigenen CSV-Parser.

Ihr erster Gedanke ist: „Ich muss Kommas innerhalb von Anführungszeichen verarbeiten.“

Ihr nächster Gedanke wird sein: „Oh, Mist, ich muss Anführungszeichen innerhalb von Anführungszeichen behandeln.Entkommene Anführungszeichen.Anführungszeichen.Einzelzitate..."

Es ist ein Weg in den Wahnsinn.Schreiben Sie nicht Ihre eigenen.Finden Sie eine Bibliothek mit einer umfangreichen Unit-Test-Abdeckung, die alle schwierigen Teile abdeckt und für Sie durch die Hölle gegangen ist.Verwenden Sie für .NET die kostenlose Version FileHelpers Bibliothek.

Python:

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

Natürlich ist die Verwendung eines CSV-Parsers besser, aber aus Spaß könnten Sie Folgendes tun:

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 

Der Autor hier hat einen Blob von C#-Code eingefügt, der das Szenario behandelt, mit dem Sie ein Problem haben:

CSV-Dateiimporte in .Net

Sollte nicht zu schwer zu übersetzen sein.

Was ist, wenn eine ungerade Anzahl von Zitaten in der ursprünglichen Zeichenfolge angezeigt wird?

Dies ähnelt auf unheimliche Weise dem CSV-Parsing, das einige Besonderheiten bei der Verarbeitung von Feldern in Anführungszeichen aufweist.Das Feld wird nur maskiert, wenn das Feld durch doppelte Anführungszeichen begrenzt wird, also:

Feld1, „Feld2, Feld3“, Feld4, „Feld5, Feld6“ Feld7

wird

Feld1

Feld2, Feld3

Feld4

"field5

Feld6" Feld7

Beachten Sie, dass es sich nicht um ein in Anführungszeichen gesetztes Feld handelt und die doppelten Anführungszeichen einfach als doppelte Anführungszeichen behandelt werden, wenn es nicht sowohl mit einem Anführungszeichen beginnt als auch endet.

Wenn ich mich richtig erinnere, geht mein Code, auf den jemand verlinkt hat, damit nicht richtig um.

Hier ist eine einfache Python-Implementierung basierend auf Pats Pseudocode:

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

Ich verwende dies zum Analysieren von Zeichenfolgen und bin mir nicht sicher, ob es hier hilft.aber vielleicht mit ein paar kleinen Modifikationen?

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

Dies ist eine Standardanalyse im CSV-Stil.Viele Leute versuchen dies mit regulären Ausdrücken zu erreichen.Mit regulären Ausdrücken können Sie etwa 90 % erreichen, aber Sie benötigen wirklich einen echten CSV-Parser, um es richtig zu machen.Ich habe einen ... gefunden Schneller, ausgezeichneter C#-CSV-Parser auf CodeProject vor ein paar Monaten, das ich wärmstens empfehlen kann!

Hier ist einer im Pseudocode (auch bekannt alsPython) in einem Durchgang :-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'])

Hier ist ein einfacher Algorithmus:

  1. Bestimmen Sie, ob die Zeichenfolge mit a beginnt '"' Charakter
  2. Teilen Sie die Zeichenfolge in ein durch das getrenntes Array auf '"' Charakter.
  3. Markieren Sie die zitierten Kommas mit einem Platzhalter #COMMA#
    • Wenn die Eingabe mit a beginnt '"', Markieren Sie die Elemente im Array, bei denen der Index % 2 == 0 ist
    • Andernfalls markieren Sie die Elemente im Array, bei denen der Index % 2 == 1 ist
  4. Verketten Sie die Elemente im Array, um eine geänderte Eingabezeichenfolge zu bilden.
  5. Teilen Sie die Zeichenfolge in ein durch das getrenntes Array auf ',' Charakter.
  6. Ersetzt alle Instanzen im Array von #COMMA# Platzhalter mit dem ',' Charakter.
  7. Das Array ist Ihre Ausgabe.

Hier ist die Python-Implementierung:
(behoben, um „a,b“,c,“d,e,f,h“,i,j,k“ zu verarbeiten)

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']

Ich konnte einfach nicht widerstehen, zu sehen, ob ich es in einem Python-Einzeiler zum Laufen bringen könnte:

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

Gibt ['a', 'string, with', 'various', 'values, and some', 'quoted'] zurück

Es funktioniert, indem es zuerst die 'Inside Zitate an einen anderen Separator (|) ersetzt, die Zeichenfolge auf "auf" aufgeteilt "und das Ersetzen der | ersetzen Trennzeichen wieder.

Da Sie sprachunabhängig sagten, habe ich meinen Algorithmus in der Sprache geschrieben, die dem Pseudocode möglichst am nächsten kommt:

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top