2009-07-01 20 views
19

me encontré con este script Perl, mientras migrating my SQLite database to mysqlLa traducción de Perl a Python

Me preguntaba (ya no sé Perl) ¿cómo se podría volver a escribir esto en Python?

puntos de bonificación para el más corto (código) respuesta :)

edición: lo siento, destinados código más corto, la respuesta no es estrictamente menor

#! /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; 
    } 
} 

Algunos código adicional era necesario migrar con éxito el sqlite base de datos (maneja una línea Crear instrucciones de tabla, claves foráneas, corrige un error en el programa original que convirtió los campos vacíos '' en \'.

I posted the code on the migrating my SQLite database to mysql Question

+17

¿Es esto tan difícil de leer? Este es probablemente el programa perl más legible jamás escrito. –

+7

dice mucho sobre perl;) – Jiaaro

+5

@John Kugelman Creo que hubiera sido aún más legible sin la variable '$ line'. –

Respuesta

47

Aquí hay una traducción bastante literal con solo el mínimo de cambios de estilo obvios (poniendo todo el código en una función, usando una cadena en lugar de reoperaciones donde sea posible).

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() 
+12

Me encanta obtener código en las respuestas ... Siempre aprendo algo nuevo :) – Jiaaro

+8

¡Siempre estoy feliz de responder con código cuando sea posible - la manera más concisa y menos ambigua, después de todo! -) –

+1

Podría estar equivocado, pero no debería sea ​​line.replace ('THIS_IS_FALSE', '0') – Jiaaro

3

Basado en http://docs.python.org/dev/howto/regex.html ...

  1. Sustituir $line =~ /.*/ con re.search(r".*", line).
  2. $line !~ /.*/ es solo !($line =~ /.*/).
  3. Reemplace $line =~ s/.*/x/g con line=re.sub(r".*", "x", line).
  4. Reemplazar $1 a través de $9 dentro de re.sub con \1 a través de \9 respectivamente.
  5. Fuera de un submarino, guardar el valor de retorno, es decir m=re.search(), y reemplazar $1 con el valor de retorno de m.group(1).
  6. Para "INSERT INTO $1$2\n" específicamente, puede hacer "INSERT INTO %s%s\n" % (m.group(1), m.group(2)).
3

No estoy seguro de qué es tan difícil de entender acerca de esto que requiere un comentario sarcástico como en su comentario anterior. Tenga en cuenta que <> se llama operador de diamantes. s/// es el operador de sustitución y // es el operador de coincidencia m//.

1

¿Más corto? La tilde significa una expresión regular en perl. "import re" e ir desde allí. Las únicas diferencias clave son que usará \ 1 y \ 2 en lugar de $ 1 y $ 2 cuando asigne valores, y usará% s para cuando reemplace las coincidencias de expresiones regulares dentro de cadenas.

1

El verdadero problema es ¿sabes realmente cómo migrar la base de datos? Lo que se presenta es simplemente un bucle de búsqueda y reemplazo.

6

Aquí hay una versión un poco mejor del original.

#! /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; 
} 
+1

Una forma mejor sería analizar realmente la entrada y enviarla en el formato correcto. –

+0

Inicialmente parecía que una exportación SQL de sqlite sería lo suficientemente fácil como para dar masajes a un formato compatible con MySQL, pero cuanto más tiempo paso en esto, más estoy de acuerdo contigo :) – Jiaaro

12

Alex Martelli's solution above funciona bien, pero necesita algunas correcciones y adiciones:

En las líneas utilizando sustitución expresión regular, la inserción de los grupos emparejados debe ser de doble escape o la cadena de sustitución debe tener el prefijo r para marcar es como expresión regular:

line = re.sub(r"([^'])'t'(.)", "\\1THIS_IS_TRUE\\2", line) 

o

line = re.sub(r"([^'])'f'(.)", r"\1THIS_IS_FALSE\2", line) 

Además, esta línea debe añadirse antes de impresión:

line = line.replace('AUTOINCREMENT', 'AUTO_INCREMENT') 

pasado, los nombres de columna en crear declaraciones deben ser invertidas en MySQL. Añadir esto en la línea 15:

sub = sub.replace('"','`') 

Aquí está el guión completo con modificaciones:

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() 
+0

Acabo de notar esto después de copiar su script y encontrar el mismo error. (Editado la respuesta anterior por lo que ahora es correcto). –

+0

Se requieren dos correcciones más para migrar una placa phpBB: 1) escama invertida, y 2) 'mediatext (n)' -> 'mediumtext'; – davide

5

todas las secuencias en esta página no puede hacer frente a simples 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'); 

Ninguno estaba capaz de reformatear "table_name" en mysql's `table_name`. Algunos arruinaron el valor de la cadena vacía.

Cuestiones relacionadas