import io, re BS = chr(92) # Как MySQL экранирует спецсимволы в дампе. Всё, чего здесь нет (\' \" \\ и т.п.), # подставляется как есть — снимается только сам слэш. ESCAPES = { 'n': '\n', 'r': '\r', 't': '\t', '0': '\0', 'b': '\b', 'Z': chr(26), } def columns(path, table): cols, inside = [], False for line in io.open(path, encoding='utf-8', errors='replace'): if line.startswith('CREATE TABLE `%s`' % table): inside = True continue if inside: m = re.match(r'\s+`([^`]+)`', line) if m: cols.append(m.group(1)) elif line.startswith(')'): break return cols def _scan(s, start=0): """Scan one (...) row starting at s[start]=='('. Returns (fields, next_index).""" assert s[start] == '(' i = start + 1 fields, buf, q = [], [], False while i < len(s): c = s[i] if q: if c == BS: # Escape-последовательности MySQL нужно раскодировать, а не просто # снимать слэш: иначе \r\n превращался в буквы "rn" прямо в тексте. nxt = s[i + 1] if i + 1 < len(s) else '' buf.append(ESCAPES.get(nxt, nxt)) i += 2 continue if c == "'": q = False i += 1 continue buf.append(c) i += 1 continue if c == "'": q = True i += 1 continue if c == ',': fields.append(''.join(buf)) buf = [] i += 1 continue if c == ')': fields.append(''.join(buf)) return fields, i + 1 buf.append(c) i += 1 return None, i def rows(path, table): cols = columns(path, table) text = [] cur = False for line in io.open(path, encoding='utf-8', errors='replace'): if re.match(r'INSERT INTO `%s` VALUES' % re.escape(table), line): cur = True text.append(line.split('VALUES', 1)[1]) continue if cur: text.append(line) if line.rstrip().endswith(';'): cur = False s = ''.join(text) i = 0 while i < len(s): if s[i] == '(': fields, i = _scan(s, i) if fields is None: break yield dict(zip(cols, [f.strip() for f in fields])) else: i += 1