Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Nombres válidos para tablas paradox (Propiedad TableName) (https://www.clubdelphi.com/foros/showthread.php?t=86585)

Yugo 05-09-2014 00:02:12

Nombres válidos para tablas paradox (Propiedad TableName)
 
La cuestión es que al crear una tabla paradox en runtime, trato de que el nombre de la misma sea introducido por el usuario de la aplicación mediante un InputQuery. El caso es que para validar dicho nombre, compruebo que no sea una cadena vacía y que si su nombre ya coincide con el nombre de otra tabla, nos de la posibilidad de sobreescribir. Sin embargo, cabe la posibilidad de que el usuario introduzca caracteres no válidos para el nombre de una tabla y me gustaría preguntar de qué manera atajo esa posibilidad. Si podeis también facilitarme dónde puedo ver las restricciones de caracteres no válidos os lo agradecería, ya que creo que son '\', '/' y '.', pero no sé si hay alguno más.

Código:

    String nombreTabla = ""; //Inicializamos a cadena vacía

    TTable *nuevaTabla = new TTable(this);
    nuevaTabla->Active = false;
    nuevaTabla->DatabaseName = ruta_db;
    nuevaTabla->TableType = ttParadox;

    //Proceso para asignar el nombre que recibirá la Tabla:
    if(InputQuery("Asignar nombre de la tabla","Introduzca el nombre de la nueva tabla",nombreTabla))
    {
      if(!nombreTabla.IsEmpty()) //Si el nombre de la tabla no es una CADENA VACÍA
      {
          if(Comprobamos si nombreTabla es válido)
          {
            nuevaTabla->TableName = nombreTabla; //Asignamos el nombre de la Tabla
            //Aquí irían la definición de los campos
            nuevaTabla->CreateTable(); //Finalmente, se crea la Tabla
          }
          else
          {
            //Volver a intentarlo de nuevo avisando de los caracteres no válidos
          }
      }
    }


ecfisa 05-09-2014 04:58:22

Hola Yugo.

Revisa este ejemplo:
Código:

/* Crear tabla */
void TablaCreate(String tableName) {
  TTable *tb = new TTable(NULL);

  tb->Close();
  tb->DatabaseName = ExtractFilePath(Application->ExeName); // ruta
  tb->TableName = tableName;
  tb->TableType = ttParadox;
  tb->FieldDefs->Clear();
  tb->FieldDefs->Add("ID", ftAutoInc, 0, false);
  tb->FieldDefs->Add("NOMBRE", ftString, 30, false);
  tb->FieldDefs->Add("DOMICILIO", ftString, 30, false);
  tb->FieldDefs->Add("TELEFONO", ftString, 15, false);
  tb->IndexDefs->Clear();
  tb->IndexDefs->Add("", "ID", TIndexOptions()<< ixPrimary << ixUnique);
  tb->IndexDefs->Add("INDNOMBRE", "NOMBRE", TIndexOptions()<< ixCaseInsensitive);
  tb->CreateTable();

  delete tb;
}

/* Verificar nombre */
bool isValidName(String str) {
  if (str == "" || (str[1]>='0'&&str[1]<='9'))
    return false;

  for(int i=1; i< str.Length(); i++) {
    char c = UpCase(str[i]);
    if(!((c>='A'&&c<='Z') || (c>='0'&&c<='9') || (c=='_')))
      return false;
  }

  return true;
}

/* Ejemplo de uso */
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  String name;
  bool ok;
  do {
    if(InputQuery("Crear tabla", "Nombre", name))
      ok = isValidName(name);
  } while ((name != "") && !ok);

  if (name != "")
    TablaCreate(name);
}

Saludos :)

aguml 05-09-2014 15:41:10

Supongo que para introducir el nombre usas un TEdit ¿Por que no usas el evento OnKeyPress para filtrar las pulsaciones de teclas? Con eso te aseguras que no se introduzcan caracteres invalidos.

aguml 05-09-2014 15:44:59

si sabes que son solo esas tres pues if(Key == '/' ll Key == '\\' ll Key == '.') Key = 0;

Yugo 06-09-2014 02:10:46

Qué tal ecfisa, tu código resulta muy cómo la verdad. Creo que sería interesante poder crear un módulo para poder insertarlo en el proyecto y de esta forma, si hay que recurrir varias veces a realizar dicha comprobación, ahorrarnos algo de código.... voy a tratar de hacerlo para exponerlo aquí. Espero, que me des permiso para usar tu código como ejemplo.

Por cierto, creo que se te olvidó poner el signo <= en la condición del bucle for que he resaltado en tu código. Si sólo ponemos < el bucle no recorrería la cadena String al completo, le faltaría el último carácter, ¿no?

Código:

/* Verificar nombre */
bool isValidName(String str) {
  if (str == "" || (str[1]>='0'&&str[1]<='9'))
    return false;

  for(int i=1; i<=str.Length(); i++) { 
    char c = UpCase(str[i]);
    if(!((c>='A'&&c<='Z') || (c>='0'&&c<='9') || (c=='_')))
      return false;
  }

  return true;
}

PD: Hay que ver que aún no le he cogido el truco a la opción de citar... se descuadra todo el codigo, jajajaja

Yugo 06-09-2014 02:19:23

Hola aguml, qué tal todo!! gracias por tu consejo. Seguro que lo probaré. Mi intención en este caso es conseguir el nombre mediante un InputQuery, pero es bueno saber de otras opciones.


Cita:

Supongo que para introducir el nombre usas un TEdit ¿Por que no usas el evento OnKeyPress para filtrar las pulsaciones de teclas? Con eso te aseguras que no se introduzcan caracteres invalidos.

si sabes que son solo esas tres pues if(Key == '/' ll Key == '\\' ll Key == '.') Key = 0;

ecfisa 06-09-2014 03:01:45

Hola Yugo.
Cita:

Empezado por Yugo (Mensaje 480789)
Por cierto, creo que se te olvidó poner el signo <= en la condición del bucle for que he resaltado en tu código. Si sólo ponemos < el bucle no recorrería la cadena String al completo, le faltaría el último carácter, ¿no?

Es correcto ;)

Y como te comenta aguml, lo normal es usar un TEdit donde se puede evaluar mucho mas fácilmente los caracteres ingresados, pero como comentaste que usarías un InputQuery, quise seguir la línea...

Saludos :)

ecfisa 06-09-2014 06:19:38

Hola de nuevo.

Para no dejar las cosas por la mitad, una implementación para usar con un TEdit:
Código:

// Grupo de caracteres no validos para nombre
const
  char* wrongKey = "|°¬!\"#$%&/()='?\\¿¡@´¨+*~{[^}]`<>,;.:-";

// Limpiar Edit ingreso de nombre
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  Edit1->Clear();
}

// Verificar en escritura
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
  TEdit *ed = static_cast<TEdit*>(Sender);

  if((ed->SelStart+ed->SelLength == 0 && Key>=0x30 && Key<=0x39) ||
    StrPos(wrongKey, String(Key).c_str()) != NULL)
    Key=0;
}

// Verificar por copiado/pegado
void __fastcall TForm1::Edit1Exit(TObject *Sender)
{
  TEdit *ed = static_cast<TEdit*>(Sender);

  for(int i = 1; i <= ed->Text.Length();i++)
    if (StrPos(wrongKey, ed->Text.c_str()+i) != NULL) {
      MessageBeep(MB_ICONERROR);
      ed->SetFocus();
    }
}

Saludos :)

aguml 06-09-2014 12:23:11

muy interesante pero hay algo que no entiendo bien: if((ed->SelStart+ed->SelLength == 0 && Key>=0x30 && Key<=0x39)... La primera no entiendo que comprueba y la segunda ¿No se admiten numeros en el nombre? ¿Puedes explicar esa zona?

ecfisa 06-09-2014 17:45:32

Hola aguml.

Cuando la suma de las propiedades SelLength y SelStart es igual a cero, estamos situados en el primer caracter y un nombre de tabla no puede comenzar por por un dígito numérico (0x33='0', 0x39='9'). De allí en más esta condición no se cumple y son permitidos los dígitos numéricos.

Saludos :)

aguml 06-09-2014 18:04:47

Muy interesante tu código y no sabía que un nombre de tabla no podia empezar con un número. ¿eso es solo en paradox?
Por cierto no sabía de la existencia de StrPos, yo siempre tiraba de strstr en C y en builder de AnsiString().Pos. Me gusta mucho el modo en que compruebas si el caracter es uno de los no deseados ya que yo hubiese tirado de muuuchos if. Nunca se me pasó por la cabeza esa opcion.
Otra cosa, ¿por qué haces esto en vez de usar directamente el nombre del componente?
TEdit *ed = static_cast<TEdit*>(Sender);
if((ed->SelStart+ed->SelLength == 0...

en vez de:
if((Edit1->SelStart+Edit1->SelLength == 0...

Le vería sentido para una funcion a la cual le pasas el puntero pero en un evento del componente donde puedes usar su nombre ya que no es algo que se vaya a usar en varios componentes diferentes...

aguml 06-09-2014 18:38:07

Aquí si le veo sentido:
Código:

const char* wrongKey = "|°¬!\"#$%&/()='?\\¿¡@´¨+*~{[^}]`<>,;.:-";

//---------------------------------------------------------------------------

// Limpiar Edit ingreso de nombre
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  Edit1->Clear();
  Edit2->Clear();
  Edit3->Clear();
}
//---------------------------------------------------------------------------

// Verificar en escritura
void VerifyKey(TObject *Sender, char &Key)
{
  TEdit *ed = static_cast<TEdit*>(Sender);

  if((ed->SelStart+ed->SelLength == 0 && isdigit(Key)) ||
    StrPos(wrongKey, String(Key).c_str()) != NULL)
    Key=0;
}
//---------------------------------------------------------------------------

// Verificar por copiado/pegado
void VerifyOnPaste(TObject *Sender)
{
  TEdit *ed = static_cast<TEdit*>(Sender);

  for(int i = 1; i <= ed->Text.Length();i++)
    if (StrPos(wrongKey, ed->Text.c_str()+i) != NULL || isdigit(ed->Text[1])) {
      MessageBox(Application->Handle,AnsiString("Una cadena no válida fue pegada en " + ed->Name + ".\nRevísela antes de continuar.").c_str(),"Atención",MB_OK | MB_ICONERROR);
      ed->SetFocus();
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
        VerifyKey(Sender,Key);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Edit2KeyPress(TObject *Sender, char &Key)
{
        VerifyKey(Sender,Key);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Edit3KeyPress(TObject *Sender, char &Key)
{
        VerifyKey(Sender,Key);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Edit1Exit(TObject *Sender)
{
        VerifyOnPaste(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Edit2Exit(TObject *Sender)
{
        VerifyOnPaste(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Edit3Exit(TObject *Sender)
{
        VerifyOnPaste(Sender);
}
//---------------------------------------------------------------------------

Ya que si son muchos TEdits nos ahorramos codigo y es mas facil entenderlo. ¿que opinas?

ecfisa 06-09-2014 18:57:48

Hola aguml.
Cita:

Empezado por aguml (Mensaje 480806)
Ya que si son muchos TEdits nos ahorramos codigo y es mas facil entenderlo. ¿que opinas?

Opino que se ahorra mas código y es mas natural asignar la función miembro:
Código:

void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
  TEdit *ed = static_cast<TEdit*>(Sender);

  if((ed->SelStart+ed->SelLength == 0 && Key>=0x30 && Key<=0x39) ||
    StrPos(wrongKey, String(Key).c_str()) != NULL)
    Key=0;
}

a los eventos OnKeyPress de todos los Edits que nos interese evaluar.

Lo mismo cuenta para la función miembro "Edit1Exit".

Saludos :)

aguml 06-09-2014 19:55:08

pues tienes toda la razon, no habia pensado en esa opcion. En ese caso seria mejor crearnos un nombre mas estandar pero es algo simplemente porque el nombre no haga referencia a un tedit, solo eso. Por ejemplo EditKeyPress y EditExit.

aguml 06-09-2014 22:50:12

ecfisa tu código para cuando sales del TEdit tiene algo mal, he estado mirandolo y yo al final lo he dejado así y funciona:
Código:

// Verificar por copiado/pegado
// El mismo evento lo asignamos a todos los TEdits que queramos filtrar
void __fastcall TForm1::EditsExit(TObject *Sender)
{
  TEdit *ed = static_cast<TEdit*>(Sender);

  //Si el TEdit no está vacío entramos
  if(ed->Text != "")
  {
    //Si el primer caracter es un número en este caso no nos vale la cadena pegada
    if(isdigit(ed->Text[1])){
      MessageBox(Application->Handle,AnsiString("Una cadena no válida fue pegada en " + ed->Name + ".\nRevísela antes de continuar.").c_str(),"Atención",MB_OK | MB_ICONERROR);
      ed->SetFocus();
    }else{
      //Si el primer caracter no es numérico comprobamos que la cadena no contenga caracteres inválidos para nuestro propósito recorriendola entera
      for(int i = 1; i <= ed->Text.Length();i++)
      {
        if (StrPos(wrongKey, String(ed->Text[i]).c_str()) != NULL) {
          MessageBox(Application->Handle,AnsiString("Una cadena no válida fue pegada en " + ed->Name + ".\nRevísela antes de continuar.").c_str(),"Atención",MB_OK | MB_ICONERROR);
          ed->SetFocus();
          break;
        }
      }
    }
  }
}
//---------------------------------------------------------------------------

El problema es que si metes el primer caracter inválido de la lista que es '|' no lo detecta o he visto cosas raras como que si es el primero si lo detecta pero si no lo es y tiene mas caracteres detras tampoco lo detecta. Ahora no recuerdo cuales eran los casos pero en mi código si funciona. Mira a ver que falla en el tuyo. Creo que ademas en tu código, en el for, i tendria que empezar valiendo 0 y el <= tendria que ser solo < ya que si no te saltas el primer caracter malo de la lista wrongKey. Ademas si encuentras un caracter malo tienes que terminar el bucle ya que recorrería toda la cadena aunque encuentre un caracter de los de wrongKey.
Corrijeme en lo que veas que estoy equivocado.

ecfisa 07-09-2014 22:57:47

Hola aguml.

Si tenes razón, la función falla en el caso que comentas. Y la tuya también lo hace cuando detecta un error al salir del Edit. Luego de mostrar el mensaje y asignarle nuevamente el foco y estándo el texto selecionado, permite la introducción de un número ya que los valores de SelStart y SelLength no son actualizados.

Sin embargo, pude comprobar que se simplifica el tratamiento con el uso de String:
Código:

...

String wrongChar  = "|°¬!\"#$%&/()='?\\¿¡@´¨+*~{[^}]`<>,;.:-";

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  for(int i=0;i < ComponentCount; i++)
    if(Components[i]->ClassNameIs("TEdit"))
      (static_cast<TEdit*>(Components[i]))->Clear();
}

void __fastcall TForm1::EditKeyPress(TObject *Sender, char &Key)
{
  TEdit *ed = static_cast<TEdit*>(Sender);

  if(ed->SelStart+ed->SelLength == 0 && (char)Key>='0' && (char)Key<='9' ||
    wrongChar.AnsiPos((char)Key)!=0) {
    MessageBeep(MB_ICONERROR);
    Key=0;
  }
}

void __fastcall TForm1::EditExit(TObject *Sender)
{
  TEdit *ed = static_cast<TEdit*>(Sender);
  bool invalid = false;

  if(ed->Text == "") return;

  if(ed->Text[1]>='0'&&ed->Text[1]<='9'|| wrongChar.AnsiPos(ed->Text[1])!=0)
    invalid = true;

  for(int i = 2; i <= ed->Text.Length(); i++)
    if(wrongChar.AnsiPos(ed->Text[i]))
      invalid = true;

  if (invalid) {
    MessageBeep(MB_ICONERROR);
    ed->SetFocus();
    ed->SelStart = 0;
    ed->SelLength = 0;
  }
}

Saludos :)

aguml 07-09-2014 23:24:48

interesante. Mañana lo pruebo y te cuento.

aguml 08-09-2014 11:46:17

Bien, me ha servido para orientarme en mi código y ver otro fallo más. ¿qué pasa si seleccionamos el primer caracter o un rango que contenga el primer caracter y pulsamos un número? pues que el número se escribe porque SelLength no es igual a 0 y lo he solucionando cambiando el == por un >= y con eso da igual la cantidad de caracteres que seleccione que si el primer caracter es uno de ellos no dejará que se introduzca un número.
Así quedó mi código:
Código:

const char* wrongKey = "|°¬!\"#$%&/()='?\\¿¡@´¨+*~{[^}]`<>,;.:-";

//---------------------------------------------------------------------------

// Verificar en escritura
// El mismo evento lo asignamos a todos los TEdits que queramos filtrar
void __fastcall TForm1::EditsKeyPress(TObject *Sender, char &Key)
{
  TEdit *ed = static_cast<TEdit*>(Sender);

  // Si es el primer caracter  o si hay una seleccion que abarque al primer caracter
  // y este es numérico o si la tecla pulsada es uno de los caracteres prohibidos
  // evitamos que se imprima el caracter
  if((ed->SelStart == 0 && ed->SelLength >= 0 && isdigit(Key)) ||
    StrPos(wrongKey, String(Key).c_str()) != NULL)
  {
    Key=0;
    MessageBeep(0);
  }
}
//---------------------------------------------------------------------------

// Verificar por copiado/pegado
// El mismo evento lo asignamos a todos los TEdits que queramos filtrar
void __fastcall TForm1::EditsExit(TObject *Sender)
{
  TEdit *ed = static_cast<TEdit*>(Sender);
  bool invalid = false;

  // Si el TEdit no está vacío entramos
  if(ed->Text != "")
  {
    // Si el primer caracter es un número en este caso no nos vale la cadena pegada
    if(isdigit(ed->Text[1])){
      invalid = true;
    }else{
      // Si el primer caracter no es numérico comprobamos que la cadena no contenga caracteres inválidos para nuestro propósito recorriendola entera
      for(int i = 1; i <= ed->Text.Length();i++)
      {
        if (StrPos(wrongKey, String(ed->Text[i] ).c_str()) != NULL) {
          // Si se encuentra un caracter inválido
          invalid = true;
          // Salimos del bucle
          break;
        }
      }
    }

    if(invalid)
    {
      // Mostramos un mensaje avisando
      MessageBox(Application->Handle,AnsiString("Una cadena no válida fue pegada en " + ed->Name + ".\nRevísela antes de continuar.").c_str(),"Atención",MB_OK | MB_ICONERROR);
      // Devolvemos el foco al TEdit que contiene la cadena inválida
      ed->SetFocus();
      ed->SelStart = 0;
      ed->SelLength = 0;
    }
  }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  //Limpiamos todos los TEdits
  for(int i=0;i < ComponentCount; i++)
    if(Components[i]->ClassNameIs("TEdit"))
      (static_cast<TEdit*>(Components[i]))->Clear();
}
//---------------------------------------------------------------------------

Puedes ver que muchisimas cosas son copiadas o adaptadas de tu código porque me parecen muy bien hechas y he guardado este proyecto porque es bueno tener algo en que fijarse cuando lo necesite ya que cambiando cuatro cosas esto es muy estandarizable para cualquier otro proyecto.

ecfisa 08-09-2014 16:03:15

Cita:

Empezado por aguml (Mensaje 480850)
...lo he solucionando cambiando el == por un >= ...

Perfecto ^\||/

Saludos :)

ecfisa 08-09-2014 16:14:41

Y ya que estamos refinando la operación, estaba viendo que esta parte:
Código PHP:

     for(int i 1<= ed->Text.Length();i++)
      {
        if (
StrPos(wrongKeyString(ed->Text[i] ).c_str()) != NULL) {
          
// Si se encuentra un caracter inválido
          
invalid true;
          
// Salimos del bucle
          
break;
        } 

Se puede escribir:
Código PHP:

     for(int i 1<= ed->Text.Length() && !invalidi++)
        if (
StrPos(wrongKeyString(ed->Text[i] ).c_str()) != NULL)
          
invalid true

Saludos :)


La franja horaria es GMT +2. Ahora son las 04:21:29.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi