Skip to content

Commit 9b95b68

Browse files
authored
Merge pull request #18 from davidanthoff/streams
Add support for loading and saving from streams
2 parents 89ef217 + 0a41298 commit 9b95b68

File tree

6 files changed

+103
-18
lines changed

6 files changed

+103
-18
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# CSVFiles.jl v0.5.0
2+
* Support for FileIO Stream objects
3+
14
# CSVFiles.jl v0.4.1
25
* Various small bug fixes
36

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,13 @@ plot(load("data.csv"), x=:a, y=:b, Geom.line)
4949
````
5050

5151
One can load both local files and files that can be downloaded via either http or https. To download
52-
from a remote URL, simply pass a URL to the ``load`` function instead of just a filename.
52+
from a remote URL, simply pass a URL to the ``load`` function instead of just a filename. In addition
53+
one can also load data from an ``IO`` object, i.e. any stream. The syntax
54+
that scenario is
55+
56+
````julia
57+
df = DataFrame(load(Stream(format"CSV", io)))
58+
````
5359

5460
The ``load`` function also takes a number of parameters:
5561

@@ -80,6 +86,13 @@ save("output.csv", it)
8086
````
8187
This will work as long as ``it`` is any of the types supported as sources in [IterableTables.jl](https://github.com/davidanthoff/IterableTables.jl).
8288

89+
One can also save into an arbitrary stream:
90+
````julia
91+
using FileIO, CSVFiles
92+
93+
save(Stream(format"CSV", io), it)
94+
````
95+
8396
The ``save`` function takes a number of arguments:
8497
````julia
8598
save(f::FileIO.File{FileIO.format"CSV"}, data; delim=',', quotechar='"', escapechar='\\', header=true)

REQUIRE

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
julia 0.6
2-
TextParse 0.1.6
3-
TableTraits 0.0.1
4-
TableTraitsUtils 0.0.1
2+
TextParse 0.4.0
3+
IteratorInterfaceExtensions 0.0.2
4+
TableTraits 0.0.3
5+
TableTraitsUtils 0.1.3
56
DataValues 0.1.0
67
FileIO 0.4.0
78
HTTP 0.6.0
8-
IterableTables 0.5.0
9+
IterableTables 0.6.1

src/CSVFiles.jl

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module CSVFiles
22

3-
using TextParse, TableTraits, TableTraitsUtils, DataValues
3+
using TextParse, IteratorInterfaceExtensions, TableTraits, TableTraitsUtils,
4+
DataValues
45
import FileIO
56
using HTTP
67
import IterableTables
@@ -11,6 +12,12 @@ struct CSVFile
1112
keywords
1213
end
1314

15+
struct CSVStream
16+
io
17+
delim
18+
keywords
19+
end
20+
1421
function load(f::FileIO.File{FileIO.format"CSV"}, delim=','; args...)
1522
return CSVFile(f.filename, delim, args)
1623
end
@@ -19,9 +26,20 @@ function load(f::FileIO.File{FileIO.format"TSV"}, delim='\t'; args...)
1926
return CSVFile(f.filename, delim, args)
2027
end
2128

22-
TableTraits.isiterable(x::CSVFile) = true
29+
function load(s::FileIO.Stream{FileIO.format"CSV"}, delim=','; args...)
30+
return CSVStream(s.io, delim, args)
31+
end
32+
33+
function load(s::FileIO.Stream{FileIO.format"TSV"}, delim='\t'; args...)
34+
return CSVStream(s.io, delim, args)
35+
end
36+
37+
IteratorInterfaceExtensions.isiterable(x::CSVFile) = true
2338
TableTraits.isiterabletable(x::CSVFile) = true
2439

40+
IteratorInterfaceExtensions.isiterable(x::CSVStream) = true
41+
TableTraits.isiterabletable(x::CSVStream) = true
42+
2543
function TableTraits.getiterator(file::CSVFile)
2644
if startswith(file.filename, "https://") || startswith(file.filename, "http://")
2745
response = HTTP.get(file.filename)
@@ -36,10 +54,22 @@ function TableTraits.getiterator(file::CSVFile)
3654
return it
3755
end
3856

57+
function TableTraits.getiterator(s::CSVStream)
58+
res = TextParse.csvread(s.io, s.delim, s.keywords...)
59+
60+
it = TableTraitsUtils.create_tableiterator([i for i in res[1]], [Symbol(i) for i in res[2]])
61+
62+
return it
63+
end
64+
3965
function Base.collect(x::CSVFile)
4066
return collect(getiterator(x))
4167
end
4268

69+
function Base.collect(x::CSVStream)
70+
return collect(getiterator(x))
71+
end
72+
4373
include("csv_writer.jl")
4474

4575
end # module

src/csv_writer.jl

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,31 @@ end
4646
end
4747
end
4848

49-
function _save(filename, data; delim=',', quotechar='"', escapechar='\\', header=true)
49+
function _save(io, data; delim=',', quotechar='"', escapechar='\\', header=true)
5050
isiterabletable(data) || error("Can't write this data to a CSV file.")
5151

5252
it = getiterator(data)
5353
colnames = TableTraits.column_names(it)
5454

5555
quotechar_internal = quotechar==nothing ? Nullable{Char}() : Nullable{Char}(quotechar)
5656

57-
open(filename, "w") do io
58-
if header
59-
if isnull(quotechar_internal)
60-
join(io,[string(colname) for colname in colnames],delim)
61-
else
62-
join(io,["$(quotechar)" *replace(string(colname), quotechar, "$(escapechar)$(quotechar)") * "$(quotechar)" for colname in colnames],delim)
63-
end
64-
println(io)
57+
if header
58+
if isnull(quotechar_internal)
59+
join(io,[string(colname) for colname in colnames],delim)
60+
else
61+
join(io,["$(quotechar)" *replace(string(colname), quotechar, "$(escapechar)$(quotechar)") * "$(quotechar)" for colname in colnames],delim)
6562
end
66-
_writecsv(io, it, eltype(it), delim, quotechar_internal, escapechar)
67-
end
63+
println(io)
64+
end
65+
_writecsv(io, it, eltype(it), delim, quotechar_internal, escapechar)
66+
end
67+
68+
function _save(filename::AbstractString, data; delim=',', quotechar='"', escapechar='\\', header=true)
69+
isiterabletable(data) || error("Can't write this data to a CSV file.")
70+
71+
open(filename, "w") do io
72+
_save(io, data, delim=delim, quotechar=quotechar, escapechar=escapechar, header=header)
73+
end
6874
end
6975

7076
function save(f::FileIO.File{FileIO.format"CSV"}, data; delim=',', quotechar='"', escapechar='\\', header=true)
@@ -74,3 +80,11 @@ end
7480
function save(f::FileIO.File{FileIO.format"TSV"}, data; delim='\t', quotechar='"', escapechar='\\', header=true)
7581
return _save(f.filename, data, delim=delim, quotechar=quotechar, escapechar=escapechar, header=header)
7682
end
83+
84+
function save(s::FileIO.Stream{FileIO.format"CSV"}, data; delim=',', quotechar='"', escapechar='\\', header=true)
85+
return _save(s.io, data, delim=delim, quotechar=quotechar, escapechar=escapechar, header=header)
86+
end
87+
88+
function save(s::FileIO.Stream{FileIO.format"TSV"}, data; delim='\t', quotechar='"', escapechar='\\', header=true)
89+
return _save(s.io, data, delim=delim, quotechar=quotechar, escapechar=escapechar, header=header)
90+
end

test/runtests.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,29 @@ finally
6666
rm(output_filename4)
6767
end
6868

69+
data = [@NT(Name="John",Age=34.,Children=2),@NT(Name="Sally",Age=54.,Children=1),@NT(Name="Jim",Age=23.,Children=0)]
70+
71+
stream = IOBuffer()
72+
mark(stream)
73+
fileiostream = FileIO.Stream(format"CSV", stream)
74+
save(fileiostream, data)
75+
reset(stream)
76+
csvstream = load(fileiostream)
77+
reloaded_data = collect(csvstream)
78+
@test isiterable(csvstream)
79+
@test isiterabletable(csvstream)
80+
@test reloaded_data == data
81+
82+
stream = IOBuffer()
83+
mark(stream)
84+
fileiostream = FileIO.Stream(format"TSV", stream)
85+
save(fileiostream, data)
86+
reset(stream)
87+
csvstream = load(fileiostream)
88+
reloaded_data = collect(csvstream)
89+
@test isiterable(csvstream)
90+
@test isiterabletable(csvstream)
91+
@test reloaded_data == data
6992

7093
end
94+

0 commit comments

Comments
 (0)