Вопрос
Я нашел этот сценарий Perl, пока перенос моей базы данных SQLite в MySQL
Мне было интересно (поскольку я не знаю Perl), как можно переписать это на Python?
Бонусные баллы за самый короткий (кодовый) ответ :)
редактировать:извините, я имел в виду кратчайший код, а не строго кратчайший ответ
#! /usr/bin/perl
while ($line = <>){
if (($line !~ /BEGIN TRANSACTION/) && ($line !~ /COMMIT/) && ($line !~ /sqlite_sequence/) && ($line !~ /CREATE UNIQUE INDEX/)){
if ($line =~ /CREATE TABLE \"([a-z_]*)\"(.*)/){
$name = $1;
$sub = $2;
$sub =~ s/\"//g; #"
$line = "DROP TABLE IF EXISTS $name;\nCREATE TABLE IF NOT EXISTS $name$sub\n";
}
elsif ($line =~ /INSERT INTO \"([a-z_]*)\"(.*)/){
$line = "INSERT INTO $1$2\n";
$line =~ s/\"/\\\"/g; #"
$line =~ s/\"/\'/g; #"
}else{
$line =~ s/\'\'/\\\'/g; #'
}
$line =~ s/([^\\'])\'t\'(.)/$1THIS_IS_TRUE$2/g; #'
$line =~ s/THIS_IS_TRUE/1/g;
$line =~ s/([^\\'])\'f\'(.)/$1THIS_IS_FALSE$2/g; #'
$line =~ s/THIS_IS_FALSE/0/g;
$line =~ s/AUTOINCREMENT/AUTO_INCREMENT/g;
print $line;
}
}
Для успешной миграции базы данных sqlite потребовался некоторый дополнительный код (обрабатывает одну строку. Операторы создания таблицы, внешние ключи, исправляет ошибку в исходной программе, которая преобразовывала пустые поля. ''
к \'
.
я опубликовал код переноса моей базы данных SQLite в MySQL. Вопрос
Решение
Вот довольно дословный перевод с минимумом очевидных изменений стиля (помещение всего кода в функцию, использование строк, а не повторных операций, где это возможно).
import re, fileinput
def main():
for line in fileinput.input():
process = False
for nope in ('BEGIN TRANSACTION','COMMIT',
'sqlite_sequence','CREATE UNIQUE INDEX'):
if nope in line: break
else:
process = True
if not process: continue
m = re.search('CREATE TABLE "([a-z_]*)"(.*)', line)
if m:
name, sub = m.groups()
line = '''DROP TABLE IF EXISTS %(name)s;
CREATE TABLE IF NOT EXISTS %(name)s%(sub)s
'''
line = line % dict(name=name, sub=sub)
else:
m = re.search('INSERT INTO "([a-z_]*)"(.*)', line)
if m:
line = 'INSERT INTO %s%s\n' % m.groups()
line = line.replace('"', r'\"')
line = line.replace('"', "'")
line = re.sub(r"([^'])'t'(.)", r"\1THIS_IS_TRUE\2", line)
line = line.replace('THIS_IS_TRUE', '1')
line = re.sub(r"([^'])'f'(.)", r"\1THIS_IS_FALSE\2", line)
line = line.replace('THIS_IS_FALSE', '0')
line = line.replace('AUTOINCREMENT', 'AUTO_INCREMENT')
print line,
main()
Другие советы
Решение Алекса Мартелли выше работает хорошо, но требует некоторых исправлений и дополнений:
В строках, использующих подстановку регулярных выражений, вставка совпадающих групп должна иметь двойное экранирование ИЛИ строка замены должна иметь префикс r, чтобы пометить ее как регулярное выражение:
line = re.sub(r"([^'])'t'(.)", "\\1THIS_IS_TRUE\\2", line)
или
line = re.sub(r"([^'])'f'(.)", r"\1THIS_IS_FALSE\2", line)
Кроме того, эту строку следует добавить перед печатью:
line = line.replace('AUTOINCREMENT', 'AUTO_INCREMENT')
Наконец, имена столбцов в операторах создания должны быть обратными кавычками в MySQL.Добавьте это в строку 15:
sub = sub.replace('"','`')
Вот полный скрипт с изменениями:
import re, fileinput
def main():
for line in fileinput.input():
process = False
for nope in ('BEGIN TRANSACTION','COMMIT',
'sqlite_sequence','CREATE UNIQUE INDEX'):
if nope in line: break
else:
process = True
if not process: continue
m = re.search('CREATE TABLE "([a-z_]*)"(.*)', line)
if m:
name, sub = m.groups()
sub = sub.replace('"','`')
line = '''DROP TABLE IF EXISTS %(name)s;
CREATE TABLE IF NOT EXISTS %(name)s%(sub)s
'''
line = line % dict(name=name, sub=sub)
else:
m = re.search('INSERT INTO "([a-z_]*)"(.*)', line)
if m:
line = 'INSERT INTO %s%s\n' % m.groups()
line = line.replace('"', r'\"')
line = line.replace('"', "'")
line = re.sub(r"([^'])'t'(.)", "\\1THIS_IS_TRUE\\2", line)
line = line.replace('THIS_IS_TRUE', '1')
line = re.sub(r"([^'])'f'(.)", "\\1THIS_IS_FALSE\\2", line)
line = line.replace('THIS_IS_FALSE', '0')
line = line.replace('AUTOINCREMENT', 'AUTO_INCREMENT')
if re.search('^CREATE INDEX', line):
line = line.replace('"','`')
print line,
main()
Вот немного улучшенная версия оригинала.
#! /usr/bin/perl
use strict;
use warnings;
use 5.010; # for s/\K//;
while( <> ){
next if m'
BEGIN TRANSACTION |
COMMIT |
sqlite_sequence |
CREATE UNIQUE INDEX
'x;
if( my($name,$sub) = m'CREATE TABLE \"([a-z_]*)\"(.*)' ){
# remove "
$sub =~ s/\"//g; #"
$_ = "DROP TABLE IF EXISTS $name;\nCREATE TABLE IF NOT EXISTS $name$sub\n";
}elsif( /INSERT INTO \"([a-z_]*)\"(.*)/ ){
$_ = "INSERT INTO $1$2\n";
# " => \"
s/\"/\\\"/g; #"
# " => '
s/\"/\'/g; #"
}else{
# '' => \'
s/\'\'/\\\'/g; #'
}
# 't' => 1
s/[^\\']\K\'t\'/1/g; #'
# 'f' => 0
s/[^\\']\K\'f\'/0/g; #'
s/AUTOINCREMENT/AUTO_INCREMENT/g;
print;
}
все скрипты на этой странице не могут работать с простым sqlite3:
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE Filename (
FilenameId INTEGER,
Name TEXT DEFAULT '',
PRIMARY KEY(FilenameId)
);
INSERT INTO "Filename" VALUES(1,'');
INSERT INTO "Filename" VALUES(2,'bigfile1');
INSERT INTO "Filename" VALUES(3,'%gconf-tree.xml');
Ни один из них не смог переформатировать «table_name» в правильное `table_name` MySQL.Некоторые перепутали пустое строковое значение.
Я не уверен, что в этом такого сложного понять, что требуется ехидное замечание, как в вашем комментарии выше.Обратите внимание, что <>
называется алмазным оператором. s///
является оператором замены и //
это оператор сопоставления m//
.
На основе http://docs.python.org/dev/howto/regex.html ...
- Заменять
$line =~ /.*/
сre.search(r".*", line)
. $line !~ /.*/
просто!($line =~ /.*/)
.- Заменять
$line =~ s/.*/x/g
сline=re.sub(r".*", "x", line)
. - Заменять
$1
через$9
внутриre.sub
с\1
через\9
соответственно. - Вне подпрограммы сохраните возвращаемое значение, т.е.
m=re.search()
, и заменить$1
с возвращаемым значениемm.group(1)
. - Для
"INSERT INTO $1$2\n"
в частности, вы можете сделать"INSERT INTO %s%s\n" % (m.group(1), m.group(2))
.
Реальная проблема заключается в том, знаете ли вы, как перенести базу данных?То, что представлено, представляет собой просто цикл поиска и замены.
Самый короткий?Тильда означает регулярное выражение в Perl.«импортировать повторно» и идти оттуда.Единственное ключевое отличие состоит в том, что вы будете использовать \1 и \2 вместо $1 и $2 при присвоении значений, а %s — при замене совпадений с регулярными выражениями внутри строк.