22

我在将我的 SQLite 数据库迁移到 mysql时发现了这个 Perl 脚本

我想知道(因为我不知道 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 数据库需要一些额外的代码(处理一行 Create table 语句,外键,修复原始程序中将空字段转换''\'.

发布了将我的 SQLite 数据库迁移到 mysql 的代码

4

8 回答 8

53

这是一个非常直接的翻译,只有最少的明显样式更改(将所有代码放入一个函数中,尽可能使用字符串而不是重新操作)。

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()
于 2009-07-01T03:25:54.410 回答
12

Alex Martelli 的上述解决方案效果很好,但需要一些修复和补充:

在使用正则表达式替换的行中,匹配组的插入必须双转义或替换字符串必须以 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()
于 2010-09-09T10:55:41.730 回答
8

此页面上的所有脚本都无法处理简单的 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”重新格式化为正确的 mysql 的 `table_name` 。一些搞砸了空字符串值。

于 2011-06-14T15:48:07.027 回答
8

这是原版的稍微好一点的版本。

#! /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;
}
于 2009-07-01T17:48:28.203 回答
3

基于http://docs.python.org/dev/howto/regex.html ...

  1. 替换$line =~ /.*/re.search(r".*", line)
  2. $line !~ /.*/只是!($line =~ /.*/)
  3. 替换$line =~ s/.*/x/gline=re.sub(r".*", "x", line)
  4. $1through $9inside分别re.sub替换为\1through \9
  5. 在 sub 之外,保存返回值,即m=re.search(),并替换$1为 的返回值m.group(1)
  6. 具体来说"INSERT INTO $1$2\n",您可以执行"INSERT INTO %s%s\n" % (m.group(1), m.group(2)).
于 2009-07-01T03:22:55.070 回答
3

我不确定这有什么难以理解的,以至于需要像您上面的评论中那样讽刺评论。请注意,这<>称为菱形运算符。s///是替换运算符并且//是匹配运算符m//

于 2009-07-01T03:24:32.493 回答
2

真正的问题是你真的知道如何迁移数据库吗?所呈现的只是一个搜索和替换循环。

于 2009-07-01T12:33:18.943 回答
1

最短?波浪号表示 perl 中的正则表达式。“导入重新”并从那里开始。唯一的关键区别是,当您分配值时,您将使用 \1 和 \2 而不是 $1 和 $2,并且当您替换字符串中的正则表达式匹配时,您将使用 %s。

于 2009-07-01T03:26:16.920 回答