We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CSVReader does not handle quoted fields or tsv files
### Summary
I want to add functionality to the CSVReader so it can handle tsv files. While checking that out, I noticed that we're just splitting on `,`, which is not ideal.

### Actual behavior
Parsing CSV files with fields that contain quoted text with ',' characters does not work correctly. TSV files are not supported.


### Expected behavior
Quoted fields should be correctly parsed if they contain the delimiter.

### How to replicate the behavior
Run the script below.

### Script/Workflow details

```
from nipype.interfaces import utility

reader = utility.CSVReader()

lines = ["foo,\"hello,world\",300.1\n"]

with open("testcsv.csv", "w") as fid:
    fid.writelines(lines)
    fid.flush()

reader.inputs.in_file = "testcsv.csv"
out = reader.run()

assert out.outputs.column_1 == ["hello, world"]
```

### Platform details:

<!-- Please run the following code from your shell and place the output between the triple ticks, below.
python -c "import nipype; from pprint import pprint; pprint(nipype.get_info())"
-->

```
{'commit_hash': 'c46a957bd',
 'commit_source': 'repository',
 'networkx_version': '3.2.1',
 'nibabel_version': '5.2.1',
 'nipype_version': '1.8.7.dev0',
 'numpy_version': '1.26.4',
 'pkg_path': '/home/USER/Desktop/nipype/nipype',
 'scipy_version': '1.12.0',
 'sys_executable': '/home/USER/Desktop/nipype/.venv/bin/python',
 'sys_platform': 'linux',
 'sys_version': '3.9.18 (main, Feb  1 2024, 11:48:51) \n[GCC 11.4.0]',
 'traits_version': '6.3.2'}

```

### Execution environment
- My python environment outside container


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nipype/interfaces/utility/csv.py`
Content:
```
1 # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2 # vi: set ft=python sts=4 ts=4 sw=4 et:
3 """CSV Handling utilities
4 """
5 from ..base import traits, TraitedSpec, DynamicTraitedSpec, File, BaseInterface
6 from ..io import add_traits
7 
8 
9 class CSVReaderInputSpec(DynamicTraitedSpec, TraitedSpec):
10     in_file = File(
11         exists=True, mandatory=True, desc="Input comma-seperated value (CSV) file"
12     )
13     header = traits.Bool(
14         False, usedefault=True, desc="True if the first line is a column header"
15     )
16 
17 
18 class CSVReader(BaseInterface):
19     """
20     Examples
21     --------
22 
23     >>> reader = CSVReader()  # doctest: +SKIP
24     >>> reader.inputs.in_file = 'noHeader.csv'  # doctest: +SKIP
25     >>> out = reader.run()  # doctest: +SKIP
26     >>> out.outputs.column_0 == ['foo', 'bar', 'baz']  # doctest: +SKIP
27     True
28     >>> out.outputs.column_1 == ['hello', 'world', 'goodbye']  # doctest: +SKIP
29     True
30     >>> out.outputs.column_2 == ['300.1', '5', '0.3']  # doctest: +SKIP
31     True
32 
33     >>> reader = CSVReader()  # doctest: +SKIP
34     >>> reader.inputs.in_file = 'header.csv'  # doctest: +SKIP
35     >>> reader.inputs.header = True  # doctest: +SKIP
36     >>> out = reader.run()  # doctest: +SKIP
37     >>> out.outputs.files == ['foo', 'bar', 'baz']  # doctest: +SKIP
38     True
39     >>> out.outputs.labels == ['hello', 'world', 'goodbye']  # doctest: +SKIP
40     True
41     >>> out.outputs.erosion == ['300.1', '5', '0.3']  # doctest: +SKIP
42     True
43 
44     """
45 
46     input_spec = CSVReaderInputSpec
47     output_spec = DynamicTraitedSpec
48     _always_run = True
49 
50     def _append_entry(self, outputs, entry):
51         for key, value in zip(self._outfields, entry):
52             outputs[key].append(value)
53         return outputs
54 
55     def _parse_line(self, line):
56         line = line.replace("\n", "")
57         entry = [x.strip() for x in line.split(",")]
58         return entry
59 
60     def _get_outfields(self):
61         with open(self.inputs.in_file) as fid:
62             entry = self._parse_line(fid.readline())
63             if self.inputs.header:
64                 self._outfields = tuple(entry)
65             else:
66                 self._outfields = tuple(["column_" + str(x) for x in range(len(entry))])
67         return self._outfields
68 
69     def _run_interface(self, runtime):
70         self._get_outfields()
71         return runtime
72 
73     def _outputs(self):
74         return self._add_output_traits(super()._outputs())
75 
76     def _add_output_traits(self, base):
77         return add_traits(base, self._get_outfields())
78 
79     def _list_outputs(self):
80         outputs = self.output_spec().get()
81         isHeader = True
82         for key in self._outfields:
83             outputs[key] = []  # initialize outfields
84         with open(self.inputs.in_file) as fid:
85             for line in fid.readlines():
86                 if self.inputs.header and isHeader:  # skip header line
87                     isHeader = False
88                     continue
89                 entry = self._parse_line(line)
90                 outputs = self._append_entry(outputs, entry)
91         return outputs
92
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/nipype/interfaces/utility/csv.py b/nipype/interfaces/utility/csv.py
--- a/nipype/interfaces/utility/csv.py
+++ b/nipype/interfaces/utility/csv.py
@@ -2,6 +2,7 @@
 # vi: set ft=python sts=4 ts=4 sw=4 et:
 """CSV Handling utilities
 """
+import csv
 from ..base import traits, TraitedSpec, DynamicTraitedSpec, File, BaseInterface
 from ..io import add_traits
 
@@ -13,6 +14,7 @@
     header = traits.Bool(
         False, usedefault=True, desc="True if the first line is a column header"
     )
+    delimiter = traits.String(",", usedefault=True, desc="Delimiter to use.")
 
 
 class CSVReader(BaseInterface):
@@ -52,14 +54,11 @@
             outputs[key].append(value)
         return outputs
 
-    def _parse_line(self, line):
-        line = line.replace("\n", "")
-        entry = [x.strip() for x in line.split(",")]
-        return entry
-
     def _get_outfields(self):
         with open(self.inputs.in_file) as fid:
-            entry = self._parse_line(fid.readline())
+            reader = csv.reader(fid, delimiter=self.inputs.delimiter)
+
+            entry = next(reader)
             if self.inputs.header:
                 self._outfields = tuple(entry)
             else:
@@ -82,10 +81,10 @@
         for key in self._outfields:
             outputs[key] = []  # initialize outfields
         with open(self.inputs.in_file) as fid:
-            for line in fid.readlines():
+            reader = csv.reader(fid, delimiter=self.inputs.delimiter)
+            for entry in reader:
                 if self.inputs.header and isHeader:  # skip header line
                     isHeader = False
                     continue
-                entry = self._parse_line(line)
                 outputs = self._append_entry(outputs, entry)
         return outputs

{"golden_diff": "diff --git a/nipype/interfaces/utility/csv.py b/nipype/interfaces/utility/csv.py\n--- a/nipype/interfaces/utility/csv.py\n+++ b/nipype/interfaces/utility/csv.py\n@@ -2,6 +2,7 @@\n # vi: set ft=python sts=4 ts=4 sw=4 et:\n \"\"\"CSV Handling utilities\n \"\"\"\n+import csv\n from ..base import traits, TraitedSpec, DynamicTraitedSpec, File, BaseInterface\n from ..io import add_traits\n \n@@ -13,6 +14,7 @@\n     header = traits.Bool(\n         False, usedefault=True, desc=\"True if the first line is a column header\"\n     )\n+    delimiter = traits.String(\",\", usedefault=True, desc=\"Delimiter to use.\")\n \n \n class CSVReader(BaseInterface):\n@@ -52,14 +54,11 @@\n             outputs[key].append(value)\n         return outputs\n \n-    def _parse_line(self, line):\n-        line = line.replace(\"\\n\", \"\")\n-        entry = [x.strip() for x in line.split(\",\")]\n-        return entry\n-\n     def _get_outfields(self):\n         with open(self.inputs.in_file) as fid:\n-            entry = self._parse_line(fid.readline())\n+            reader = csv.reader(fid, delimiter=self.inputs.delimiter)\n+\n+            entry = next(reader)\n             if self.inputs.header:\n                 self._outfields = tuple(entry)\n             else:\n@@ -82,10 +81,10 @@\n         for key in self._outfields:\n             outputs[key] = []  # initialize outfields\n         with open(self.inputs.in_file) as fid:\n-            for line in fid.readlines():\n+            reader = csv.reader(fid, delimiter=self.inputs.delimiter)\n+            for entry in reader:\n                 if self.inputs.header and isHeader:  # skip header line\n                     isHeader = False\n                     continue\n-                entry = self._parse_line(line)\n                 outputs = self._append_entry(outputs, entry)\n         return outputs\n", "issue": "CSVReader does not handle quoted fields or tsv files\n### Summary\r\nI want to add functionality to the CSVReader so it can handle tsv files. While checking that out, I noticed that we're just splitting on `,`, which is not ideal.\r\n\r\n### Actual behavior\r\nParsing CSV files with fields that contain quoted text with ',' characters does not work correctly. TSV files are not supported.\r\n\r\n\r\n### Expected behavior\r\nQuoted fields should be correctly parsed if they contain the delimiter.\r\n\r\n### How to replicate the behavior\r\nRun the script below.\r\n\r\n### Script/Workflow details\r\n\r\n```\r\nfrom nipype.interfaces import utility\r\n\r\nreader = utility.CSVReader()\r\n\r\nlines = [\"foo,\\\"hello,world\\\",300.1\\n\"]\r\n\r\nwith open(\"testcsv.csv\", \"w\") as fid:\r\n    fid.writelines(lines)\r\n    fid.flush()\r\n\r\nreader.inputs.in_file = \"testcsv.csv\"\r\nout = reader.run()\r\n\r\nassert out.outputs.column_1 == [\"hello, world\"]\r\n```\r\n\r\n### Platform details:\r\n\r\n<!-- Please run the following code from your shell and place the output between the triple ticks, below.\r\npython -c \"import nipype; from pprint import pprint; pprint(nipype.get_info())\"\r\n-->\r\n\r\n```\r\n{'commit_hash': 'c46a957bd',\r\n 'commit_source': 'repository',\r\n 'networkx_version': '3.2.1',\r\n 'nibabel_version': '5.2.1',\r\n 'nipype_version': '1.8.7.dev0',\r\n 'numpy_version': '1.26.4',\r\n 'pkg_path': '/home/USER/Desktop/nipype/nipype',\r\n 'scipy_version': '1.12.0',\r\n 'sys_executable': '/home/USER/Desktop/nipype/.venv/bin/python',\r\n 'sys_platform': 'linux',\r\n 'sys_version': '3.9.18 (main, Feb  1 2024, 11:48:51) \\n[GCC 11.4.0]',\r\n 'traits_version': '6.3.2'}\r\n\r\n```\r\n\r\n### Execution environment\r\n- My python environment outside container\r\n\n", "before_files": [{"content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n\"\"\"CSV Handling utilities\n\"\"\"\nfrom ..base import traits, TraitedSpec, DynamicTraitedSpec, File, BaseInterface\nfrom ..io import add_traits\n\n\nclass CSVReaderInputSpec(DynamicTraitedSpec, TraitedSpec):\n    in_file = File(\n        exists=True, mandatory=True, desc=\"Input comma-seperated value (CSV) file\"\n    )\n    header = traits.Bool(\n        False, usedefault=True, desc=\"True if the first line is a column header\"\n    )\n\n\nclass CSVReader(BaseInterface):\n    \"\"\"\n    Examples\n    --------\n\n    >>> reader = CSVReader()  # doctest: +SKIP\n    >>> reader.inputs.in_file = 'noHeader.csv'  # doctest: +SKIP\n    >>> out = reader.run()  # doctest: +SKIP\n    >>> out.outputs.column_0 == ['foo', 'bar', 'baz']  # doctest: +SKIP\n    True\n    >>> out.outputs.column_1 == ['hello', 'world', 'goodbye']  # doctest: +SKIP\n    True\n    >>> out.outputs.column_2 == ['300.1', '5', '0.3']  # doctest: +SKIP\n    True\n\n    >>> reader = CSVReader()  # doctest: +SKIP\n    >>> reader.inputs.in_file = 'header.csv'  # doctest: +SKIP\n    >>> reader.inputs.header = True  # doctest: +SKIP\n    >>> out = reader.run()  # doctest: +SKIP\n    >>> out.outputs.files == ['foo', 'bar', 'baz']  # doctest: +SKIP\n    True\n    >>> out.outputs.labels == ['hello', 'world', 'goodbye']  # doctest: +SKIP\n    True\n    >>> out.outputs.erosion == ['300.1', '5', '0.3']  # doctest: +SKIP\n    True\n\n    \"\"\"\n\n    input_spec = CSVReaderInputSpec\n    output_spec = DynamicTraitedSpec\n    _always_run = True\n\n    def _append_entry(self, outputs, entry):\n        for key, value in zip(self._outfields, entry):\n            outputs[key].append(value)\n        return outputs\n\n    def _parse_line(self, line):\n        line = line.replace(\"\\n\", \"\")\n        entry = [x.strip() for x in line.split(\",\")]\n        return entry\n\n    def _get_outfields(self):\n        with open(self.inputs.in_file) as fid:\n            entry = self._parse_line(fid.readline())\n            if self.inputs.header:\n                self._outfields = tuple(entry)\n            else:\n                self._outfields = tuple([\"column_\" + str(x) for x in range(len(entry))])\n        return self._outfields\n\n    def _run_interface(self, runtime):\n        self._get_outfields()\n        return runtime\n\n    def _outputs(self):\n        return self._add_output_traits(super()._outputs())\n\n    def _add_output_traits(self, base):\n        return add_traits(base, self._get_outfields())\n\n    def _list_outputs(self):\n        outputs = self.output_spec().get()\n        isHeader = True\n        for key in self._outfields:\n            outputs[key] = []  # initialize outfields\n        with open(self.inputs.in_file) as fid:\n            for line in fid.readlines():\n                if self.inputs.header and isHeader:  # skip header line\n                    isHeader = False\n                    continue\n                entry = self._parse_line(line)\n                outputs = self._append_entry(outputs, entry)\n        return outputs\n", "path": "nipype/interfaces/utility/csv.py"}], "after_files": [{"content": "# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-\n# vi: set ft=python sts=4 ts=4 sw=4 et:\n\"\"\"CSV Handling utilities\n\"\"\"\nimport csv\nfrom ..base import traits, TraitedSpec, DynamicTraitedSpec, File, BaseInterface\nfrom ..io import add_traits\n\n\nclass CSVReaderInputSpec(DynamicTraitedSpec, TraitedSpec):\n    in_file = File(\n        exists=True, mandatory=True, desc=\"Input comma-seperated value (CSV) file\"\n    )\n    header = traits.Bool(\n        False, usedefault=True, desc=\"True if the first line is a column header\"\n    )\n    delimiter = traits.String(\",\", usedefault=True, desc=\"Delimiter to use.\")\n\n\nclass CSVReader(BaseInterface):\n    \"\"\"\n    Examples\n    --------\n\n    >>> reader = CSVReader()  # doctest: +SKIP\n    >>> reader.inputs.in_file = 'noHeader.csv'  # doctest: +SKIP\n    >>> out = reader.run()  # doctest: +SKIP\n    >>> out.outputs.column_0 == ['foo', 'bar', 'baz']  # doctest: +SKIP\n    True\n    >>> out.outputs.column_1 == ['hello', 'world', 'goodbye']  # doctest: +SKIP\n    True\n    >>> out.outputs.column_2 == ['300.1', '5', '0.3']  # doctest: +SKIP\n    True\n\n    >>> reader = CSVReader()  # doctest: +SKIP\n    >>> reader.inputs.in_file = 'header.csv'  # doctest: +SKIP\n    >>> reader.inputs.header = True  # doctest: +SKIP\n    >>> out = reader.run()  # doctest: +SKIP\n    >>> out.outputs.files == ['foo', 'bar', 'baz']  # doctest: +SKIP\n    True\n    >>> out.outputs.labels == ['hello', 'world', 'goodbye']  # doctest: +SKIP\n    True\n    >>> out.outputs.erosion == ['300.1', '5', '0.3']  # doctest: +SKIP\n    True\n\n    \"\"\"\n\n    input_spec = CSVReaderInputSpec\n    output_spec = DynamicTraitedSpec\n    _always_run = True\n\n    def _append_entry(self, outputs, entry):\n        for key, value in zip(self._outfields, entry):\n            outputs[key].append(value)\n        return outputs\n\n    def _get_outfields(self):\n        with open(self.inputs.in_file) as fid:\n            reader = csv.reader(fid, delimiter=self.inputs.delimiter)\n\n            entry = next(reader)\n            if self.inputs.header:\n                self._outfields = tuple(entry)\n            else:\n                self._outfields = tuple([\"column_\" + str(x) for x in range(len(entry))])\n        return self._outfields\n\n    def _run_interface(self, runtime):\n        self._get_outfields()\n        return runtime\n\n    def _outputs(self):\n        return self._add_output_traits(super()._outputs())\n\n    def _add_output_traits(self, base):\n        return add_traits(base, self._get_outfields())\n\n    def _list_outputs(self):\n        outputs = self.output_spec().get()\n        isHeader = True\n        for key in self._outfields:\n            outputs[key] = []  # initialize outfields\n        with open(self.inputs.in_file) as fid:\n            reader = csv.reader(fid, delimiter=self.inputs.delimiter)\n            for entry in reader:\n                if self.inputs.header and isHeader:  # skip header line\n                    isHeader = False\n                    continue\n                outputs = self._append_entry(outputs, entry)\n        return outputs\n", "path": "nipype/interfaces/utility/csv.py"}]}