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データベースを正常に移行するには、いくつかの追加コードが必要でした(1行の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

上記のAlexMartelliのソリューションはうまく機能しますが、いくつかの修正と追加が必要です。

正規表現置換を使用する行では、一致するグループの挿入をダブルエスケープするか、置換文字列の前に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')

最後に、createステートメントの列名は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

これがオリジナルの少し良いバージョンです。

#! /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 に答える
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 に答える
3

http://docs.python.org/dev/howto/regex.htmlに基づく..。

  1. に置き換え$line =~ /.*/ますre.search(r".*", line)
  2. $line !~ /.*/ただ!($line =~ /.*/)です。
  3. に置き換え$line =~ s/.*/x/gますline=re.sub(r".*", "x", line)
  4. $1スルー$9インサイドre.sub\1スルーに\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 に答える