saulsolis09 commited on
Commit
e8882c6
·
verified ·
1 Parent(s): 9be1a26

Updated app.py with EOF Error Handling logic for ghost characters

Browse files
Files changed (1) hide show
  1. app.py +58 -12
app.py CHANGED
@@ -243,13 +243,56 @@ def detect_delimiter(path, candidates=(",", "|", "^", "\t", ";", "\x1f", "\x1e")
243
  return best
244
 
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  def _read_csv_auto(path, **kwargs):
247
  """pd.read_csv with the delimiter auto-detected (unless caller passed sep),
248
  tolerating malformed rows (a stray delimiter in one row won't abort the read)."""
249
  if "sep" not in kwargs:
250
  kwargs["sep"] = detect_delimiter(path)
251
  kwargs.setdefault("on_bad_lines", "skip")
252
- return pd.read_csv(path, **kwargs)
 
253
 
254
 
255
  def scan_bad_rows(path, sep, out_dir):
@@ -262,12 +305,12 @@ def scan_bad_rows(path, sep, out_dir):
262
  bw = None
263
  try:
264
  with open(path, "r", encoding="utf-8", errors="replace", newline="") as fin:
265
- header = fin.readline()
266
  if not header:
267
  return 0, None
268
  base = header.rstrip("\r\n").count(sep) # header delimiter count
269
  for lineno, line in enumerate(fin, start=2):
270
- s = line.rstrip("\r\n")
271
  if not s:
272
  continue
273
  if s.count(sep) > base: # too many fields -> skipped by pandas
@@ -850,8 +893,9 @@ def run_pipeline(uploaded_files, reference_file, blank_cities_not_in_list,
850
  try:
851
  if str(name).lower().endswith(".csv"):
852
  sep = detect_delimiter(name)
853
- dfs.append(pd.read_csv(name, low_memory=False, sep=sep,
854
- on_bad_lines="skip"))
 
855
  _step(log, f"Loaded {Path(name).name} ({len(dfs[-1]):,} rows, "
856
  f"delimiter {sep!r})")
857
  else:
@@ -1501,8 +1545,9 @@ def _split_by_county(uploaded_files, out_dir, log, chunksize=STREAM_CHUNK_ROWS):
1501
  # detect the FIPS-county column from a small sample of the first file
1502
  first = uploaded_files[0].name
1503
  if str(first).lower().endswith(".csv"):
1504
- sample = pd.read_csv(first, nrows=50_000, low_memory=False,
1505
- sep=detect_delimiter(first), on_bad_lines="skip")
 
1506
  else:
1507
  sample = pd.read_excel(first)
1508
  fips_col, reason = detect_fips_county_column(sample)
@@ -1536,11 +1581,12 @@ def _split_by_county(uploaded_files, out_dir, log, chunksize=STREAM_CHUNK_ROWS):
1536
  if str(nm).lower().endswith(".csv"):
1537
  sep = detect_delimiter(nm)
1538
  _step(log, f"Reading {Path(nm).name} (delimiter {sep!r})")
1539
- for chunk in pd.read_csv(nm, chunksize=chunksize, low_memory=False,
1540
- sep=sep, on_bad_lines="skip"):
1541
- route(chunk)
1542
- total += len(chunk)
1543
- _step(log, f"Split: routed {total:,} rows into {len(county_paths)} counties")
 
1544
  else:
1545
  chunk = pd.read_excel(nm)
1546
  route(chunk)
 
243
  return best
244
 
245
 
246
+ _CTRL_DELETE = {c: None for c in range(32)}
247
+ for _keep in (9, 10, 13): # keep tab, newline, carriage return
248
+ _CTRL_DELETE.pop(_keep, None)
249
+ _CTRL_DELETE[127] = None # DEL
250
+
251
+
252
+ def _strip_ctrl(s):
253
+ """Remove C0 control bytes (NUL, SUB/'EOF', and the rest) except tab/LF/CR.
254
+ A stray control byte embedded in a text field (e.g. an address or legal
255
+ description from the source system) otherwise either aborts pandas' C parser
256
+ or silently truncates the field at a fake end-of-file."""
257
+ return s.translate(_CTRL_DELETE)
258
+
259
+
260
+ class _CleanReader:
261
+ """Text file-like wrapper that strips control bytes as the file is read, so a
262
+ stray control character in one field can't break the parse. Works with pandas'
263
+ chunked and whole-file reads alike; opened with errors='replace' so stray
264
+ non-UTF-8 bytes don't abort the read either."""
265
+ def __init__(self, path, encoding="utf-8", errors="replace"):
266
+ self._f = open(path, "r", encoding=encoding, errors=errors, newline="")
267
+
268
+ def read(self, size=-1):
269
+ return _strip_ctrl(self._f.read(size))
270
+
271
+ def readline(self, size=-1):
272
+ return _strip_ctrl(self._f.readline(size))
273
+
274
+ def __iter__(self):
275
+ for line in self._f:
276
+ yield _strip_ctrl(line)
277
+
278
+ def close(self):
279
+ self._f.close()
280
+
281
+ def __enter__(self):
282
+ return self
283
+
284
+ def __exit__(self, *exc):
285
+ self.close()
286
+
287
+
288
  def _read_csv_auto(path, **kwargs):
289
  """pd.read_csv with the delimiter auto-detected (unless caller passed sep),
290
  tolerating malformed rows (a stray delimiter in one row won't abort the read)."""
291
  if "sep" not in kwargs:
292
  kwargs["sep"] = detect_delimiter(path)
293
  kwargs.setdefault("on_bad_lines", "skip")
294
+ with _CleanReader(path) as fh:
295
+ return pd.read_csv(fh, **kwargs)
296
 
297
 
298
  def scan_bad_rows(path, sep, out_dir):
 
305
  bw = None
306
  try:
307
  with open(path, "r", encoding="utf-8", errors="replace", newline="") as fin:
308
+ header = _strip_ctrl(fin.readline())
309
  if not header:
310
  return 0, None
311
  base = header.rstrip("\r\n").count(sep) # header delimiter count
312
  for lineno, line in enumerate(fin, start=2):
313
+ s = _strip_ctrl(line).rstrip("\r\n")
314
  if not s:
315
  continue
316
  if s.count(sep) > base: # too many fields -> skipped by pandas
 
893
  try:
894
  if str(name).lower().endswith(".csv"):
895
  sep = detect_delimiter(name)
896
+ with _CleanReader(name) as _fh:
897
+ dfs.append(pd.read_csv(_fh, low_memory=False, sep=sep,
898
+ on_bad_lines="skip"))
899
  _step(log, f"Loaded {Path(name).name} ({len(dfs[-1]):,} rows, "
900
  f"delimiter {sep!r})")
901
  else:
 
1545
  # detect the FIPS-county column from a small sample of the first file
1546
  first = uploaded_files[0].name
1547
  if str(first).lower().endswith(".csv"):
1548
+ with _CleanReader(first) as _fh:
1549
+ sample = pd.read_csv(_fh, nrows=50_000, low_memory=False,
1550
+ sep=detect_delimiter(first), on_bad_lines="skip")
1551
  else:
1552
  sample = pd.read_excel(first)
1553
  fips_col, reason = detect_fips_county_column(sample)
 
1581
  if str(nm).lower().endswith(".csv"):
1582
  sep = detect_delimiter(nm)
1583
  _step(log, f"Reading {Path(nm).name} (delimiter {sep!r})")
1584
+ with _CleanReader(nm) as _fh:
1585
+ for chunk in pd.read_csv(_fh, chunksize=chunksize, low_memory=False,
1586
+ sep=sep, on_bad_lines="skip"):
1587
+ route(chunk)
1588
+ total += len(chunk)
1589
+ _step(log, f"Split: routed {total:,} rows into {len(county_paths)} counties")
1590
  else:
1591
  chunk = pd.read_excel(nm)
1592
  route(chunk)