<p>Given a string like this:</p>

<blockquote>
  <p>a,"string, with",various,"values, and some",quoted</p>
</blockquote>

<p>What is a good algorithm to split this based on commas while ignoring the commas inside the quoted sections?</p>

<p>The output should be an array:</p>

<blockquote>
  <p>[ "a", "string, with", "various", "values, and some", "quoted" ]</p>
</blockquote>

[{'answer_id': 6215, 'author': 'Pat', 'author_id': 238, 'author_profile': 'https://Stackoverflow.com/users/238', 'pm_score': 2, 'selected': False, 'text': '<p>Of course using a CSV parser is better but just for the fun of it you could:</p>\n\n<pre><code>Loop on the string letter by letter.\n    If current_letter == quote : \n        toggle inside_quote variable.\n    Else if (current_letter ==comma and not inside_quote) : \n        push current_word into array and clear current_word.\n    Else \n        append the current_letter to current_word\nWhen the loop is done push the current_word into array \n</code></pre>\n'}, {'answer_id': 6217, 'author': 'Kev', 'author_id': 419, 'author_profile': 'https://Stackoverflow.com/users/419', 'pm_score': 1, 'selected': False, 'text': '<p>The author here dropped in a blob of C# code that handles the scenario you\'re having a problem with:</p>\n\n<p><a href="https://stackoverflow.com/questions/1898/csv-file-imports-in-net#6111">CSV File Imports in .Net</a></p>\n\n<p>Shouldn\'t be too difficult to translate.</p>\n'}, {'answer_id': 6223, 'author': 'Martin', 'author_id': 770, 'author_profile': 'https://Stackoverflow.com/users/770', 'pm_score': 3, 'selected': True, 'text': "<p>If my language of choice didn't offer a way to do this without thinking then I would initially consider two options as the easy way out:</p>\n\n<ol>\n<li><p>Pre-parse and replace the commas within the string with another control character then split them, followed by a post-parse on the array to replace the control character used previously with the commas.</p></li>\n<li><p>Alternatively split them on the commas then post-parse the resulting array into another array checking for leading quotes on each array entry and concatenating the entries until I reached a terminating quote.</p></li>\n</ol>\n\n<p>These are hacks however, and if this is a pure 'mental' exercise then I suspect they will prove unhelpful.  If this is a real world problem then it would help to know the language so that we could offer some specific advice.</p>\n"}, {'answer_id': 6237, 'author': 'mauriciopastrana', 'author_id': 547, 'author_profile': 'https://Stackoverflow.com/users/547', 'pm_score': 0, 'selected': False, 'text': '<p>I use this to parse strings, not sure if it helps here; but with some minor modifications perhaps?</p>\n\n<pre><code>function getstringbetween($string, $start, $end){\n    $string = " ".$string;\n    $ini = strpos($string,$start);\n    if ($ini == 0) return "";\n    $ini += strlen($start);   \n    $len = strpos($string,$end,$ini) - $ini;\n    return substr($string,$ini,$len);\n}\n\n$fullstring = "this is my [tag]dog[/tag]";\n$parsed = getstringbetween($fullstring, "[tag]", "[/tag]");\n\necho $parsed; // (result = dog) \n</code></pre>\n\n<p>/mp</p>\n'}, {'answer_id': 6243, 'author': 'Justin Standard', 'author_id': 92, 'author_profile': 'https://Stackoverflow.com/users/92', 'pm_score': 0, 'selected': False, 'text': '<p>Here\'s a simple algorithm:</p>\n\n<ol>\n<li>Determine if the string begins with a <code>\'"\'</code> character</li>\n<li>Split the string into an array delimited by the <code>\'"\'</code> character.</li>\n<li>Mark the quoted commas with a placeholder <code>#COMMA#</code>\n\n<ul>\n<li>If the input starts with a <code>\'"\'</code>, mark those items in the array where the index % 2 == 0</li>\n<li>Otherwise mark those items in the array where the index % 2 == 1</li>\n</ul></li>\n<li>Concatenate the items in the array to form a modified input string.</li>\n<li>Split the string into an array delimited by the <code>\',\'</code> character.</li>\n<li>Replace all instances in the array of <code>#COMMA#</code> placeholders with the <code>\',\'</code> character.</li>\n<li>The array is your output.</li>\n</ol>\n\n<p>Heres the python implementation:<br>\n(fixed to handle \'"a,b",c,"d,e,f,h","i,j,k"\')</p>\n\n<pre><code>def parse_input(input):\n\n    quote_mod = int(not input.startswith(\'"\'))\n\n    input = input.split(\'"\')\n    for item in input:\n        if item == \'\':\n            input.remove(item)\n    for i in range(len(input)):\n        if i % 2 == quoted_mod:\n            input[i] = input[i].replace(",", "#COMMA#")\n\n    input = "".join(input).split(",")\n    for item in input:\n        if item == \'\':\n            input.remove(item)\n    for i in range(len(input)):\n        input[i] = input[i].replace("#COMMA#", ",")\n    return input\n\n# parse_input(\'a,"string, with",various,"values, and some",quoted\')\n#  -&gt; [\'a,string\', \' with,various,values\', \' and some,quoted\']\n# parse_input(\'"a,b",c,"d,e,f,h","i,j,k"\')\n#  -&gt; [\'a,b\', \'c\', \'d,e,f,h\', \'i,j,k\']\n</code></pre>\n'}, {'answer_id': 6255, 'author': 'Simon Gillbee', 'author_id': 756, 'author_profile': 'https://Stackoverflow.com/users/756', 'pm_score': 0, 'selected': False, 'text': '<p>This is a standard CSV-style parse. A lot of people try to do this with regular expressions. You can get to about 90% with regexes, but you really need a real CSV parser to do it properly. I found a <a href="http://www.codeproject.com/KB/database/CsvReader.aspx" rel="nofollow noreferrer">fast, excellent C# CSV parser on CodeProject</a> a few months ago that I highly recommend!</p>\n'}, {'answer_id': 6278, 'author': 'Brian Jorgensen', 'author_id': 229, 'author_profile': 'https://Stackoverflow.com/users/229', 'pm_score': 0, 'selected': False, 'text': '<h1>Here\'s one in pseudocode (a.k.a. Python) in one pass :-P</h1>\n\n<pre><code>def parsecsv(instr):\n    i = 0\n    j = 0\n\n    outstrs = []\n\n    # i is fixed until a match occurs, then it advances\n    # up to j. j inches forward each time through:\n\n    while i &lt; len(instr):\n\n        if j &lt; len(instr) and instr[j] == \'"\':\n            # skip the opening quote...\n            j += 1\n            # then iterate until we find a closing quote.\n            while instr[j] != \'"\':\n                j += 1\n                if j == len(instr):\n                    raise Exception("Unmatched double quote at end of input.")\n\n        if j == len(instr) or instr[j] == \',\':\n            s = instr[i:j]  # get the substring we\'ve found\n            s = s.strip()    # remove extra whitespace\n\n            # remove surrounding quotes if they\'re there\n            if len(s) &gt; 2 and s[0] == \'"\' and s[-1] == \'"\':\n                s = s[1:-1]\n\n            # add it to the result\n            outstrs.append(s)\n\n            # skip over the comma, move i up (to where\n            # j will be at the end of the iteration)\n            i = j+1\n\n        j = j+1\n\n    return outstrs\n\ndef testcase(instr, expected):\n    outstr = parsecsv(instr)\n    print outstr\n    assert expected == outstr\n\n# Doesn\'t handle things like \'1, 2, "a, b, c" d, 2\' or\n# escaped quotes, but those can be added pretty easily.\n\ntestcase(\'a, b, "1, 2, 3", c\', [\'a\', \'b\', \'1, 2, 3\', \'c\'])\ntestcase(\'a,b,"1, 2, 3" , c\', [\'a\', \'b\', \'1, 2, 3\', \'c\'])\n\n# odd number of quotes gives a "unmatched quote" exception\n#testcase(\'a,b,"1, 2, 3" , "c\', [\'a\', \'b\', \'1, 2, 3\', \'c\'])\n</code></pre>\n'}, {'answer_id': 6305, 'author': 'Judah Gabriel Himango', 'author_id': 536, 'author_profile': 'https://Stackoverflow.com/users/536', 'pm_score': 4, 'selected': False, 'text': '<p>Looks like you\'ve got some good answers here.</p>\n\n<p>For those of you looking to handle your own CSV file parsing, heed the advice from the experts and <a href="http://secretgeek.net/csv_trouble.asp" rel="noreferrer">Don\'t roll your own CSV parser</a>.</p>\n\n<p>Your first thought is, <em>"I need to handle commas inside of quotes."</em></p>\n\n<p>Your next thought will be, <em>"Oh, crap, I need to handle quotes inside of quotes. Escaped quotes. Double quotes. Single quotes..."</em></p>\n\n<p>It\'s a road to madness. Don\'t write your own. Find a library with an extensive unit test coverage that hits all the hard parts and has gone through hell for you. For .NET, use the free <a href="http://www.filehelpers.com/" rel="noreferrer">FileHelpers</a> library.</p>\n'}, {'answer_id': 6322, 'author': 'PabloG', 'author_id': 394, 'author_profile': 'https://Stackoverflow.com/users/394', 'pm_score': 0, 'selected': False, 'text': '<p>I just couldn\'t resist to see if I could make it work in a Python one-liner:</p>\n\n<pre><code>arr = [i.replace("|", ",") for i in re.sub(\'"([^"]*)\\,([^"]*)"\',"\\g&lt;1&gt;|\\g&lt;2&gt;", str_to_test).split(",")]\n</code></pre>\n\n<p>Returns [\'a\', \'string, with\', \'various\', \'values, and some\', \'quoted\']\n<p>It works by first replacing the \',\' inside quotes to another separator (|), \nsplitting the string on \',\' and replacing the | separator again.</p>\n'}, {'answer_id': 6386, 'author': 'Mark Harrison', 'author_id': 116, 'author_profile': 'https://Stackoverflow.com/users/116', 'pm_score': 3, 'selected': False, 'text': '<p><strong>Python:</strong></p>\n\n<pre><code>import csv\nreader = csv.reader(open("some.csv"))\nfor row in reader:\n    print row\n</code></pre>\n'}, {'answer_id': 6437, 'author': 'ICR', 'author_id': 214, 'author_profile': 'https://Stackoverflow.com/users/214', 'pm_score': 1, 'selected': False, 'text': '<blockquote>\n  <p>What if an odd number of quotes appear\n  in the original string?</p>\n</blockquote>\n\n<p>This looks uncannily like CSV parsing, which has some peculiarities to handling quoted fields. The field is only escaped if the field is delimited with double quotations, so:</p>\n\n<blockquote>\n  <p>field1, "field2, field3", field4, "field5, field6" field7</p>\n</blockquote>\n\n<p>becomes</p>\n\n<blockquote>\n  <p>field1</p>\n  \n  <p>field2, field3</p>\n  \n  <p>field4</p>\n  \n  <p>"field5</p>\n  \n  <p>field6" field7</p>\n</blockquote>\n\n<p>Notice if it doesn\'t both start and end with a quotation, then it\'s not a quoted field and the double quotes are simply treated as double quotes.</p>\n\n<p>Insedently my code that someone linked to doesn\'t actually handle this correctly, if I recall correctly.</p>\n'}, {'answer_id': 3861098, 'author': 'Greg Bayer', 'author_id': 466482, 'author_profile': 'https://Stackoverflow.com/users/466482', 'pm_score': 1, 'selected': False, 'text': '<p>Here\'s a simple python implementation based on Pat\'s pseudocode:</p>\n\n<pre><code>def splitIgnoringSingleQuote(string, split_char, remove_quotes=False):\n    string_split = []\n    current_word = ""\n    inside_quote = False\n    for letter in string:\n      if letter == "\'":\n        if not remove_quotes:\n           current_word += letter\n        if inside_quote:\n          inside_quote = False\n        else:\n          inside_quote = True\n      elif letter == split_char and not inside_quote:\n        string_split.append(current_word)\n        current_word = ""\n      else:\n        current_word += letter\n    string_split.append(current_word)\n    return string_split\n</code></pre>\n'}, {'answer_id': 52808987, 'author': 'Goodword', 'author_id': 3999494, 'author_profile': 'https://Stackoverflow.com/users/3999494', 'pm_score': 0, 'selected': False, 'text': '<p>Since you said language agnostic, I wrote my algorithm in the language that\'s closest to pseudocode as posible:</p>\n\n<pre><code>def find_character_indices(s, ch):\n    return [i for i, ltr in enumerate(s) if ltr == ch]\n\n\ndef split_text_preserving_quotes(content, include_quotes=False):\n    quote_indices = find_character_indices(content, \'"\')\n\n    output = content[:quote_indices[0]].split()\n\n    for i in range(1, len(quote_indices)):\n        if i % 2 == 1: # end of quoted sequence\n            start = quote_indices[i - 1]\n            end = quote_indices[i] + 1\n            output.extend([content[start:end]])\n\n        else:\n            start = quote_indices[i - 1] + 1\n            end = quote_indices[i]\n            split_section = content[start:end].split()\n            output.extend(split_section)\n\n        output += content[quote_indices[-1] + 1:].split()                                                                 \n\n    return output\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/6209', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/-1/']