root/lang/ruby/wema/lib/csvparser.rb

Revision 3489, 5.8 kB (checked in by kan, 6 years ago)

imported wema (sticky wiki)

Line 
1# = CSVParser - CSV format parser / generator
2# CSVParser is a library to parse and generate CSV(Comma Sepalated Value) format
3# text data. It can do TSV(Tab Sepalated Value) format text data with tab as sepalate
4# delimiter.
5#
6# == License
7# CSVParser is a free software distributed under the Ruby license.
8#
9# == Author
10# SUZUKI Tetsuya <suzuki@spice-of-life.net>
11
12
13require 'delegate'
14
15# CSVParser is a class to parse and generate CSV format.
16# Instance methods of Array delegates to lines attribute.
17class CSVParser < DelegateClass(Array)
18        include Enumerable
19
20        # String to parse.
21        attr_accessor :string
22
23        # Array of CSV lines(CSVLine objects).
24        attr_accessor :lines
25
26        class << self
27                # Generates an instance parsed contents of the file.
28                def new_with_file( filename, space = false, sepalator = ',' )
29                        file   = File.new filename
30                        string = file.read
31                        file.close
32                        CSVParser.new( string, space, sepalator )
33                end
34
35                # Returns an array parsed the string.
36                def parse( string, space = false, sepalator = ',' )
37                        in_field  = :IN_FIELD
38                        in_qfield = :IN_QFIELD
39
40                        status = in_field
41                        csv    = string.split //
42                        parsed = []
43       
44                        char = index = nil
45                        line = ''
46
47                        csv.each_with_index do | char, index |
48                                case status
49                                when in_field
50                                        case char
51                                        when '"'
52                                                line << char
53                                                status = in_qfield
54                                        when "\r"
55                                                unless csv[index+1] == "\n" then
56                                                        parsed << _parse_line(line,space,sepalator) unless line.empty?
57                                                        line = ''
58                                                end
59                                        when "\n"
60                                                parsed << _parse_line(line,space,sepalator) unless line.empty?
61                                                line = ''
62                                        else
63                                                line << char
64                                        end
65                                when in_qfield
66                                        case char
67                                        when '"'
68                                                line << char
69                                                status = in_field
70                                        else
71                                                line << char
72                                        end
73                                end
74                        end
75
76                        parsed << _parse_line(line,space,sepalator) unless line.empty?
77                        parsed
78                end
79
80                private
81                def _parse_line( string, space, sepalator )
82                        in_field  = :IN_FIELD
83                        in_qfield = :IN_QFIELD
84
85                        status = in_field
86                        csv    = string.split //
87                        parsed = []
88       
89                        char = index = match = nil
90                        field = ''
91                        quote = 0
92                        is_quote = false
93
94                        csv.each_with_index do | char, index |
95                                case status
96                                when in_field
97                                        case char
98                                        when '"'
99                                                unless is_quote then
100                                                        field = ''
101                                                        is_quote = true
102                                                else
103                                                        quote += 1
104                                                end
105                                                status = in_qfield
106
107                                                if (quote > 0) and ((quote%2) == 0) then
108                                                        field << '"'
109                                                end
110                                        when sepalator
111                                                if space then
112                                                        if match = /[ ]+$/.match(field) then field = match.pre_match end
113                                                        if match = /^[ ]+/.match(field) then field = match.post_match end
114                                                end
115                                                parsed << field
116                                                field = ''
117                                                is_quote = false
118                                                quote = 0
119                                        else
120                                                field << char unless is_quote
121                                        end
122                                when in_qfield
123                                        case char
124                                        when '"'
125                                                quote += 1
126                                                status = in_field
127                                        else
128                                                quote = 0
129                                                field << char
130                                        end
131                                end
132                        end
133
134                        if space then
135                                if match = /[ ]+$/.match(field) then field = match.pre_match end
136                                if match = /^[ ]+/.match(field) then field = match.post_match end
137                        end
138                        parsed << field
139
140                        parsed
141                end
142
143                public
144
145                # Returns an array parsed contents of the file.
146                def parse_with_file( filename, space = false, sepalator = ',' )
147                        file   = File.new filename
148                        string = file.read
149                        file.close
150                        CSVParser.parse( string, space, sepalator )
151                end
152        end
153
154        # Generates an instance parsed the string sepalated by the sepalator.
155        # If the space is true, deletes spaces of fields at start and end.
156        def initialize( string = '', space = false, sepalator = ',' )
157                @string = string
158                @lines = []
159
160                lines = CSVParser.parse( @string, space, sepalator )
161                line  = nil
162                lines.each do | line |
163                        @lines << CSVLine.new(line)
164                end
165
166                super @lines
167        end
168
169        # Executes the block for every field.
170        def each_field
171                line = field = nil
172                @lines.each do | line |
173                        line.each do | field |
174                                yield field
175                        end
176                end
177        end
178
179        # Writes saving data to the specified file.
180        def write_to_file( filename, enquote = false, eol = "\r\n", sepalator = ',' )
181                str = to_s( enquote, eol, sepalator )
182                f = File.new( filename, 'w' )
183                f.write str
184                f.close
185        end
186
187        # Returns true if self is equal to the CSVParser object.
188        def ==( csvparser )
189                if @lines == csvparser.lines then
190                        true
191                else
192                        false
193                end
194        end
195
196        # Returns a string converted to CSV format with the sepalator and eol(end of line)
197        # character. If enquote is true, each fields are enclosed with double quotes.
198        def to_s( enquote = false, eol = "\r\n", sepalator = ',' )
199                str = ''
200                @lines.each do | line |
201                        str << line.to_s(enquote,sepalator)
202                        str << eol
203                end
204                str
205        end
206end
207
208
209# CSVLine is a class to manage CSV line.
210# Instance methods of Array delegates to fields attribute.
211class CSVLine < DelegateClass(Array)
212
213        # Array of CSV fields.
214        attr_accessor :fields
215
216        class << self
217                # Returns a string escaped CSV control characters(double quotes and returns).
218                # If enquote is true, the string are enclosed with double quotes. Or the string
219                # are enclosed when it includes CSV control characters or the sepalator.
220                def escape( string, enquote = false, sepalator = ',' )
221                        escaped = string.dup
222                        if (escaped =~ /[#{sepalator}"\r\n]/) or enquote then
223                                escaped.gsub!( '"', '""' )
224                                escaped = '"' + escaped + '"'
225                        end
226                        escaped
227                end
228        end
229
230        # Generates an instance with the array as fields.
231        def initialize( array = [] )
232                @fields = []
233
234                field = nil
235                array.each do | field |
236                        @fields << field
237                end
238
239                super @fields
240        end
241
242        # Returns true if self is equal to the CSVLine object.
243        def ==( csvline )
244                if @fields == csvline.fields then
245                        true
246                else
247                        false
248                end
249        end
250
251        # Returns a string converted to CSV format with the sepalator.
252        # If enquote is true, each fields are enclosed with double quotes.
253        def to_s( enquote = false, sepalator = ',' )
254                str  = ''
255                field = nil
256
257                @fields.each do | field |
258                        str << CSVLine.escape(field,enquote,sepalator)
259                        str << sepalator
260                end
261                str.chop!
262                str
263        end
264end
265
Note: See TracBrowser for help on using the browser.