blob: ca40e5e0efc42ae7b007634c692d803df05add98 [file] [log] [blame]
Skip Montanaro04ae7052003-04-24 20:21:31 +00001
2"""
3csv.py - read/write/investigate CSV files
4"""
5
6import re
7from _csv import Error, __version__, writer, reader, register_dialect, \
8 unregister_dialect, get_dialect, list_dialects, \
Andrew McNamara31d88962005-01-12 03:45:10 +00009 field_size_limit, \
Skip Montanaro04ae7052003-04-24 20:21:31 +000010 QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE, \
11 __doc__
Andrew McNamara7130ff52005-01-11 02:22:47 +000012from _csv import Dialect as _Dialect
Skip Montanaro04ae7052003-04-24 20:21:31 +000013
Guido van Rossum68937b42007-05-18 00:51:22 +000014from io import StringIO
Skip Montanaro1448d472003-04-25 14:47:16 +000015
Skip Montanaro04ae7052003-04-24 20:21:31 +000016__all__ = [ "QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE",
Brett Cannone8d0bf92008-08-18 00:51:19 +000017 "Error", "Dialect", "__doc__", "excel", "excel_tab",
18 "field_size_limit", "reader", "writer",
Skip Montanaro04ae7052003-04-24 20:21:31 +000019 "register_dialect", "get_dialect", "list_dialects", "Sniffer",
20 "unregister_dialect", "__version__", "DictReader", "DictWriter" ]
21
22class Dialect:
Georg Brandl7424dd32010-10-27 07:27:06 +000023 """Describe a CSV dialect.
Skip Montanarof26285c2005-01-05 06:54:58 +000024
25 This must be subclassed (see csv.excel). Valid attributes are:
26 delimiter, quotechar, escapechar, doublequote, skipinitialspace,
27 lineterminator, quoting.
28
29 """
Skip Montanaro04ae7052003-04-24 20:21:31 +000030 _name = ""
31 _valid = False
32 # placeholders
33 delimiter = None
34 quotechar = None
35 escapechar = None
36 doublequote = None
37 skipinitialspace = None
38 lineterminator = None
39 quoting = None
40
41 def __init__(self):
42 if self.__class__ != Dialect:
43 self._valid = True
Andrew McNamara7130ff52005-01-11 02:22:47 +000044 self._validate()
Skip Montanaro04ae7052003-04-24 20:21:31 +000045
46 def _validate(self):
Andrew McNamara7130ff52005-01-11 02:22:47 +000047 try:
48 _Dialect(self)
Guido van Rossumb940e112007-01-10 16:19:56 +000049 except TypeError as e:
Andrew McNamara7130ff52005-01-11 02:22:47 +000050 # We do this for compatibility with py2.3
51 raise Error(str(e))
Skip Montanaro04ae7052003-04-24 20:21:31 +000052
53class excel(Dialect):
Skip Montanarof26285c2005-01-05 06:54:58 +000054 """Describe the usual properties of Excel-generated CSV files."""
Skip Montanaro04ae7052003-04-24 20:21:31 +000055 delimiter = ','
56 quotechar = '"'
57 doublequote = True
58 skipinitialspace = False
59 lineterminator = '\r\n'
60 quoting = QUOTE_MINIMAL
61register_dialect("excel", excel)
62
63class excel_tab(excel):
Skip Montanarof26285c2005-01-05 06:54:58 +000064 """Describe the usual properties of Excel-generated TAB-delimited files."""
Skip Montanaro04ae7052003-04-24 20:21:31 +000065 delimiter = '\t'
66register_dialect("excel-tab", excel_tab)
67
Georg Brandl7424dd32010-10-27 07:27:06 +000068class unix_dialect(Dialect):
69 """Describe the usual properties of Unix-generated CSV files."""
70 delimiter = ','
71 quotechar = '"'
72 doublequote = True
73 skipinitialspace = False
74 lineterminator = '\n'
75 quoting = QUOTE_ALL
76register_dialect("unix", unix_dialect)
77
Skip Montanaro04ae7052003-04-24 20:21:31 +000078
79class DictReader:
Skip Montanarodffeed32003-10-03 14:03:01 +000080 def __init__(self, f, fieldnames=None, restkey=None, restval=None,
Skip Montanaro3f7a9482003-09-06 19:52:12 +000081 dialect="excel", *args, **kwds):
Skip Montanaroaf8fcfa2008-08-09 19:44:22 +000082 self._fieldnames = fieldnames # list of keys for the dict
Skip Montanaro04ae7052003-04-24 20:21:31 +000083 self.restkey = restkey # key to catch long rows
84 self.restval = restval # default value for short rows
Skip Montanaro3f7a9482003-09-06 19:52:12 +000085 self.reader = reader(f, dialect, *args, **kwds)
Christian Heimes4fbc72b2008-03-22 00:47:35 +000086 self.dialect = dialect
87 self.line_num = 0
Skip Montanaro04ae7052003-04-24 20:21:31 +000088
89 def __iter__(self):
90 return self
91
Skip Montanaroaf8fcfa2008-08-09 19:44:22 +000092 @property
93 def fieldnames(self):
94 if self._fieldnames is None:
95 try:
96 self._fieldnames = next(self.reader)
97 except StopIteration:
98 pass
99 self.line_num = self.reader.line_num
100 return self._fieldnames
101
102 @fieldnames.setter
103 def fieldnames(self, value):
104 self._fieldnames = value
105
Georg Brandla18af4e2007-04-21 15:47:16 +0000106 def __next__(self):
Skip Montanaroaf8fcfa2008-08-09 19:44:22 +0000107 if self.line_num == 0:
108 # Used only for its side effect.
109 self.fieldnames
Georg Brandla18af4e2007-04-21 15:47:16 +0000110 row = next(self.reader)
Christian Heimes4fbc72b2008-03-22 00:47:35 +0000111 self.line_num = self.reader.line_num
Skip Montanarodffeed32003-10-03 14:03:01 +0000112
Skip Montanaro04ae7052003-04-24 20:21:31 +0000113 # unlike the basic reader, we prefer not to return blanks,
114 # because we will typically wind up with a dict full of None
115 # values
116 while row == []:
Georg Brandla18af4e2007-04-21 15:47:16 +0000117 row = next(self.reader)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000118 d = dict(zip(self.fieldnames, row))
119 lf = len(self.fieldnames)
120 lr = len(row)
121 if lf < lr:
122 d[self.restkey] = row[lf:]
123 elif lf > lr:
124 for key in self.fieldnames[lr:]:
125 d[key] = self.restval
126 return d
127
128
129class DictWriter:
130 def __init__(self, f, fieldnames, restval="", extrasaction="raise",
Skip Montanaro3f7a9482003-09-06 19:52:12 +0000131 dialect="excel", *args, **kwds):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000132 self.fieldnames = fieldnames # list of keys for the dict
133 self.restval = restval # for writing short dicts
134 if extrasaction.lower() not in ("raise", "ignore"):
Collin Winterce36ad82007-08-30 01:19:48 +0000135 raise ValueError("extrasaction (%s) must be 'raise' or 'ignore'"
136 % extrasaction)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000137 self.extrasaction = extrasaction
Skip Montanaro3f7a9482003-09-06 19:52:12 +0000138 self.writer = writer(f, dialect, *args, **kwds)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000139
R. David Murraybe0698b2010-02-23 22:57:58 +0000140 def writeheader(self):
141 header = dict(zip(self.fieldnames, self.fieldnames))
142 self.writerow(header)
143
Skip Montanaro04ae7052003-04-24 20:21:31 +0000144 def _dict_to_list(self, rowdict):
145 if self.extrasaction == "raise":
Guido van Rossumd8faa362007-04-27 19:54:29 +0000146 wrong_fields = [k for k in rowdict if k not in self.fieldnames]
147 if wrong_fields:
Collin Winterce36ad82007-08-30 01:19:48 +0000148 raise ValueError("dict contains fields not in fieldnames: "
R David Murrayfb099c92013-11-19 13:16:20 -0500149 + ", ".join([repr(x) for x in wrong_fields]))
Serhiy Storchaka7901b482015-03-30 09:09:54 +0300150 return (rowdict.get(key, self.restval) for key in self.fieldnames)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000151
152 def writerow(self, rowdict):
153 return self.writer.writerow(self._dict_to_list(rowdict))
154
155 def writerows(self, rowdicts):
Serhiy Storchaka7901b482015-03-30 09:09:54 +0300156 return self.writer.writerows(map(self._dict_to_list, rowdicts))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000157
Raymond Hettinger39a55922003-06-12 03:01:55 +0000158# Guard Sniffer's type checking against builds that exclude complex()
159try:
160 complex
161except NameError:
162 complex = float
Skip Montanaro04ae7052003-04-24 20:21:31 +0000163
164class Sniffer:
165 '''
166 "Sniffs" the format of a CSV file (i.e. delimiter, quotechar)
Skip Montanaro1448d472003-04-25 14:47:16 +0000167 Returns a Dialect object.
Skip Montanaro04ae7052003-04-24 20:21:31 +0000168 '''
Skip Montanaro1448d472003-04-25 14:47:16 +0000169 def __init__(self):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000170 # in case there is more than one possible delimiter
171 self.preferred = [',', '\t', ';', ' ', ':']
172
Skip Montanaro04ae7052003-04-24 20:21:31 +0000173
Skip Montanaro77892372003-05-19 15:33:36 +0000174 def sniff(self, sample, delimiters=None):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000175 """
Skip Montanaro1448d472003-04-25 14:47:16 +0000176 Returns a dialect (or None) corresponding to the sample
Skip Montanaro04ae7052003-04-24 20:21:31 +0000177 """
Skip Montanaro04ae7052003-04-24 20:21:31 +0000178
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000179 quotechar, doublequote, delimiter, skipinitialspace = \
Skip Montanaro77892372003-05-19 15:33:36 +0000180 self._guess_quote_and_delimiter(sample, delimiters)
Skip Montanaro39b29be2005-12-30 05:09:48 +0000181 if not delimiter:
Skip Montanaro77892372003-05-19 15:33:36 +0000182 delimiter, skipinitialspace = self._guess_delimiter(sample,
183 delimiters)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000184
Skip Montanaro39b29be2005-12-30 05:09:48 +0000185 if not delimiter:
Collin Winterce36ad82007-08-30 01:19:48 +0000186 raise Error("Could not determine delimiter")
Skip Montanaro39b29be2005-12-30 05:09:48 +0000187
Skip Montanaro1448d472003-04-25 14:47:16 +0000188 class dialect(Dialect):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000189 _name = "sniffed"
190 lineterminator = '\r\n'
Fred Drake7c852f32003-04-25 14:27:00 +0000191 quoting = QUOTE_MINIMAL
Skip Montanaro04ae7052003-04-24 20:21:31 +0000192 # escapechar = ''
Skip Montanaro04ae7052003-04-24 20:21:31 +0000193
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000194 dialect.doublequote = doublequote
Skip Montanaro1448d472003-04-25 14:47:16 +0000195 dialect.delimiter = delimiter
196 # _csv.reader won't accept a quotechar of ''
197 dialect.quotechar = quotechar or '"'
198 dialect.skipinitialspace = skipinitialspace
199
200 return dialect
Skip Montanaro04ae7052003-04-24 20:21:31 +0000201
202
Skip Montanaro77892372003-05-19 15:33:36 +0000203 def _guess_quote_and_delimiter(self, data, delimiters):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000204 """
205 Looks for text enclosed between two identical quotes
206 (the probable quotechar) which are preceded and followed
207 by the same character (the probable delimiter).
208 For example:
209 ,'some text',
210 The quote with the most wins, same with the delimiter.
211 If there is no quotechar the delimiter can't be determined
212 this way.
213 """
214
215 matches = []
216 for restr in ('(?P<delim>[^\w\n"\'])(?P<space> ?)(?P<quote>["\']).*?(?P=quote)(?P=delim)', # ,".*?",
217 '(?:^|\n)(?P<quote>["\']).*?(?P=quote)(?P<delim>[^\w\n"\'])(?P<space> ?)', # ".*?",
218 '(?P<delim>>[^\w\n"\'])(?P<space> ?)(?P<quote>["\']).*?(?P=quote)(?:$|\n)', # ,".*?"
219 '(?:^|\n)(?P<quote>["\']).*?(?P=quote)(?:$|\n)'): # ".*?" (no delim, no space)
Fred Drake6f7b2132003-09-02 16:01:07 +0000220 regexp = re.compile(restr, re.DOTALL | re.MULTILINE)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000221 matches = regexp.findall(data)
222 if matches:
223 break
224
225 if not matches:
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000226 # (quotechar, doublequote, delimiter, skipinitialspace)
227 return ('', False, None, 0)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000228 quotes = {}
229 delims = {}
230 spaces = 0
Serhiy Storchaka07360df2015-03-30 01:01:48 +0300231 groupindex = regexp.groupindex
Skip Montanaro04ae7052003-04-24 20:21:31 +0000232 for m in matches:
Serhiy Storchaka07360df2015-03-30 01:01:48 +0300233 n = groupindex['quote'] - 1
Skip Montanaro04ae7052003-04-24 20:21:31 +0000234 key = m[n]
235 if key:
236 quotes[key] = quotes.get(key, 0) + 1
237 try:
Serhiy Storchaka07360df2015-03-30 01:01:48 +0300238 n = groupindex['delim'] - 1
Skip Montanaro04ae7052003-04-24 20:21:31 +0000239 key = m[n]
240 except KeyError:
241 continue
Skip Montanaro77892372003-05-19 15:33:36 +0000242 if key and (delimiters is None or key in delimiters):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000243 delims[key] = delims.get(key, 0) + 1
244 try:
Serhiy Storchaka07360df2015-03-30 01:01:48 +0300245 n = groupindex['space'] - 1
Skip Montanaro04ae7052003-04-24 20:21:31 +0000246 except KeyError:
247 continue
248 if m[n]:
249 spaces += 1
250
Guido van Rossum89da5d72006-08-22 00:21:25 +0000251 quotechar = max(quotes, key=quotes.get)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000252
253 if delims:
Guido van Rossum89da5d72006-08-22 00:21:25 +0000254 delim = max(delims, key=delims.get)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000255 skipinitialspace = delims[delim] == spaces
256 if delim == '\n': # most likely a file with a single column
257 delim = ''
258 else:
259 # there is *no* delimiter, it's a single column of quoted data
260 delim = ''
261 skipinitialspace = 0
262
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000263 # if we see an extra quote between delimiters, we've got a
264 # double quoted format
R David Murray925a3222013-06-29 18:40:53 -0400265 dq_regexp = re.compile(
266 r"((%(delim)s)|^)\W*%(quote)s[^%(delim)s\n]*%(quote)s[^%(delim)s\n]*%(quote)s\W*((%(delim)s)|$)" % \
267 {'delim':re.escape(delim), 'quote':quotechar}, re.MULTILINE)
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +0000268
269
270
271 if dq_regexp.search(data):
272 doublequote = True
273 else:
274 doublequote = False
275
276 return (quotechar, doublequote, delim, skipinitialspace)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000277
278
Skip Montanaro77892372003-05-19 15:33:36 +0000279 def _guess_delimiter(self, data, delimiters):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000280 """
281 The delimiter /should/ occur the same number of times on
282 each row. However, due to malformed data, it may not. We don't want
283 an all or nothing approach, so we allow for small variations in this
284 number.
285 1) build a table of the frequency of each character on every line.
Ezio Melotti13925002011-03-16 11:05:33 +0200286 2) build a table of frequencies of this frequency (meta-frequency?),
Skip Montanaro04ae7052003-04-24 20:21:31 +0000287 e.g. 'x occurred 5 times in 10 rows, 6 times in 1000 rows,
288 7 times in 2 rows'
289 3) use the mode of the meta-frequency to determine the /expected/
290 frequency for that character
291 4) find out how often the character actually meets that goal
292 5) the character that best meets its goal is the delimiter
293 For performance reasons, the data is evaluated in chunks, so it can
294 try and evaluate the smallest portion of the data possible, evaluating
295 additional chunks as necessary.
296 """
297
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000298 data = list(filter(None, data.split('\n')))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000299
300 ascii = [chr(c) for c in range(127)] # 7-bit ASCII
301
302 # build frequency tables
303 chunkLength = min(10, len(data))
304 iteration = 0
305 charFrequency = {}
306 modes = {}
307 delims = {}
308 start, end = 0, min(chunkLength, len(data))
309 while start < len(data):
310 iteration += 1
311 for line in data[start:end]:
312 for char in ascii:
Skip Montanaro1448d472003-04-25 14:47:16 +0000313 metaFrequency = charFrequency.get(char, {})
Skip Montanaro04ae7052003-04-24 20:21:31 +0000314 # must count even if frequency is 0
Skip Montanaro91bb70c2005-12-28 15:37:25 +0000315 freq = line.count(char)
Skip Montanaro04ae7052003-04-24 20:21:31 +0000316 # value is the mode
Skip Montanaro1448d472003-04-25 14:47:16 +0000317 metaFrequency[freq] = metaFrequency.get(freq, 0) + 1
318 charFrequency[char] = metaFrequency
Skip Montanaro04ae7052003-04-24 20:21:31 +0000319
320 for char in charFrequency.keys():
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000321 items = list(charFrequency[char].items())
Skip Montanaro04ae7052003-04-24 20:21:31 +0000322 if len(items) == 1 and items[0][0] == 0:
323 continue
324 # get the mode of the frequencies
325 if len(items) > 1:
Guido van Rossum89da5d72006-08-22 00:21:25 +0000326 modes[char] = max(items, key=lambda x: x[1])
Skip Montanaro04ae7052003-04-24 20:21:31 +0000327 # adjust the mode - subtract the sum of all
328 # other frequencies
329 items.remove(modes[char])
330 modes[char] = (modes[char][0], modes[char][1]
Guido van Rossum89da5d72006-08-22 00:21:25 +0000331 - sum(item[1] for item in items))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000332 else:
333 modes[char] = items[0]
334
335 # build a list of possible delimiters
336 modeList = modes.items()
337 total = float(chunkLength * iteration)
338 # (rows of consistent data) / (number of rows) = 100%
339 consistency = 1.0
340 # minimum consistency threshold
341 threshold = 0.9
342 while len(delims) == 0 and consistency >= threshold:
343 for k, v in modeList:
344 if v[0] > 0 and v[1] > 0:
Skip Montanaro77892372003-05-19 15:33:36 +0000345 if ((v[1]/total) >= consistency and
346 (delimiters is None or k in delimiters)):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000347 delims[k] = v
348 consistency -= 0.01
349
350 if len(delims) == 1:
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000351 delim = list(delims.keys())[0]
Skip Montanaro04ae7052003-04-24 20:21:31 +0000352 skipinitialspace = (data[0].count(delim) ==
353 data[0].count("%c " % delim))
354 return (delim, skipinitialspace)
355
356 # analyze another chunkLength lines
357 start = end
358 end += chunkLength
359
360 if not delims:
361 return ('', 0)
362
363 # if there's more than one, fall back to a 'preferred' list
364 if len(delims) > 1:
365 for d in self.preferred:
366 if d in delims.keys():
367 skipinitialspace = (data[0].count(d) ==
368 data[0].count("%c " % d))
369 return (d, skipinitialspace)
370
Skip Montanaro39b29be2005-12-30 05:09:48 +0000371 # nothing else indicates a preference, pick the character that
372 # dominates(?)
373 items = [(v,k) for (k,v) in delims.items()]
374 items.sort()
375 delim = items[-1][1]
376
Skip Montanaro04ae7052003-04-24 20:21:31 +0000377 skipinitialspace = (data[0].count(delim) ==
378 data[0].count("%c " % delim))
379 return (delim, skipinitialspace)
380
381
Skip Montanaro1448d472003-04-25 14:47:16 +0000382 def has_header(self, sample):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000383 # Creates a dictionary of types of data in each column. If any
384 # column is of a single type (say, integers), *except* for the first
385 # row, then the first row is presumed to be labels. If the type
386 # can't be determined, it is assumed to be a string in which case
387 # the length of the string is the determining factor: if all of the
388 # rows except for the first are the same length, it's a header.
389 # Finally, a 'vote' is taken at the end for each column, adding or
390 # subtracting from the likelihood of the first row being a header.
391
Skip Montanaro1448d472003-04-25 14:47:16 +0000392 rdr = reader(StringIO(sample), self.sniff(sample))
Skip Montanaro04ae7052003-04-24 20:21:31 +0000393
Georg Brandla18af4e2007-04-21 15:47:16 +0000394 header = next(rdr) # assume first row is header
Skip Montanaro04ae7052003-04-24 20:21:31 +0000395
396 columns = len(header)
397 columnTypes = {}
398 for i in range(columns): columnTypes[i] = None
399
400 checked = 0
Skip Montanaro1448d472003-04-25 14:47:16 +0000401 for row in rdr:
Skip Montanaro04ae7052003-04-24 20:21:31 +0000402 # arbitrary number of rows to check, to keep it sane
403 if checked > 20:
404 break
405 checked += 1
406
407 if len(row) != columns:
408 continue # skip rows that have irregular number of columns
409
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000410 for col in list(columnTypes.keys()):
Raymond Hettinger39a55922003-06-12 03:01:55 +0000411
Amaury Forgeot d'Arca4618732008-04-24 18:26:53 +0000412 for thisType in [int, float, complex]:
Skip Montanaro04ae7052003-04-24 20:21:31 +0000413 try:
Raymond Hettinger39a55922003-06-12 03:01:55 +0000414 thisType(row[col])
415 break
Raymond Hettingerabe14e62003-06-12 03:59:17 +0000416 except (ValueError, OverflowError):
Raymond Hettinger39a55922003-06-12 03:01:55 +0000417 pass
418 else:
Skip Montanaro04ae7052003-04-24 20:21:31 +0000419 # fallback to length of string
420 thisType = len(row[col])
421
422 if thisType != columnTypes[col]:
423 if columnTypes[col] is None: # add new column type
424 columnTypes[col] = thisType
425 else:
426 # type is inconsistent, remove column from
427 # consideration
428 del columnTypes[col]
429
430 # finally, compare results against first row and "vote"
431 # on whether it's a header
432 hasHeader = 0
433 for col, colType in columnTypes.items():
434 if type(colType) == type(0): # it's a length
435 if len(header[col]) != colType:
436 hasHeader += 1
437 else:
438 hasHeader -= 1
439 else: # attempt typecast
440 try:
Raymond Hettinger39a55922003-06-12 03:01:55 +0000441 colType(header[col])
Raymond Hettingerf31cb0c2003-06-12 04:05:00 +0000442 except (ValueError, TypeError):
Skip Montanaro04ae7052003-04-24 20:21:31 +0000443 hasHeader += 1
444 else:
445 hasHeader -= 1
446
447 return hasHeader > 0