[C]isNumeric (VB6 a C)

Iniciado por BlackZeroX, 16 Agosto 2011, 22:45 PM

0 Miembros y 1 Visitante están viendo este tema.

BlackZeroX

.
Se trabajan las funciones a nivel Bit, ambas funciones son mas rapidas que la nativa en vb6.

Funcion en VB6

Codigos para descargar...
Archivos Fuente dll, y Compilados



La funcion isnumeric_Black esta en VB6, es la version en vb6 de la que esta aqui...



http://foro.elhacker.net/programacion_visual_basic/reto_reemplazo_de_funcion_isnumeric-t336067.0.html;msg1652210#msg1652210

main.cpp



y aqui les dejo la traduccion en C (Las funciones IsNumericA y IsNumericW estan configuradas para trabajar con las Strings de VB6)



#include "main.h"

/**
   @Autor          BlackZeroX
   @Web            http://infrangelux.sytes.net/blog/
   @Reference      http://foro.elhacker.net/programacion_visual_basic/reto_reemplazo_de_funcion_isnumeric-t336067.0.html;msg1652210#msg1652210
   @Description    Libreria para verificar si una Cadena de texto ANSI/UNICODE es numerica con los siguientes formatos:
                   * ###e[+/-]###  {+/-646.45e+546,45}.
                   * ###d[+/-]###  {+/-646.45d+546,45}.
                   * Base 16       {&H4561abcdef}.
                   * Base 10       {+/-456,541.456,545}.
   @Returns        0 si la cadena no contiene ningun formato.
                   1 si la cadena pertenece a algun tipo de formato numerico.
**/

#define PUNTO_DECIMAL     0x10000
#define SIGNO_SRC         0x20000
#define NUMBER_HEX        0x40000
#define NUMBER_HEX_ZERO   0x80000
#define NUMBER_HEX_FLAGS  (NUMBER_HEX | NUMBER_HEX_ZERO)
#define NUMBER_POW        0x100000
#define NUMBER_POW_FINISH 0x200000
#define NUMBER_POW_FLAGS  (NUMBER_POW | NUMBER_POW_FINISH)
#define NUMBER_OF_OK      0x400000
#define NUMBER_OF_FINISH  0x800000
#define NUMBER_OF_FLAGS   (NUMBER_OF_OK | NUMBER_OF_FINISH)

DLL_EXPORT int __stdcall IsNumericA (char *pStr) { return pStr ? IsNumericEx(pStr, *((int*)(pStr - 0x4)), 1): 0; }
DLL_EXPORT int __stdcall IsNumericW (char *pStr) { return pStr ? IsNumericEx(pStr, *((int*)(pStr - 0x4)), 2): 0; }

int IsNumericEx (char *pStr, size_t uiLn, int iStep) {
unsigned int i       = 0x0,  //  For()
            iData   = 0x0;  //  Xor, Switcher, Contador (QWord).

   if (!pStr || !uiLn)
       return 0;

   for (i = 0; i < uiLn; i += iStep) {
       if ((iData & NUMBER_HEX) == NUMBER_HEX) {
           if (((*pStr >= 0x30) && (*pStr <= 0x39)) ||
              ((*pStr >= 0x61) && (*pStr <= 0x66)) ||
              ((*pStr >= 0x41) && (*pStr <= 0x46))) {  //  Numeros Hexadecimales
               if ((iData & NUMBER_OF_FLAGS) == 0x0)   //  Ceros a la izquierda
                   iData |= (*pStr == 0x30) ? NUMBER_HEX_ZERO : NUMBER_OF_OK;

               switch (iData & NUMBER_OF_FLAGS) {
                   case NUMBER_OF_OK:
                       iData++;
                       if ((iData & 0xFF) == 0x11)
                           return 0;   //  QWord (Max Double)
                       iData |= NUMBER_OF_OK;
                       if ((iData | NUMBER_HEX_FLAGS) == NUMBER_HEX_FLAGS)
                           iData ^= NUMBER_HEX_ZERO;
                       break;

                   case NUMBER_OF_FINISH:
                   case NUMBER_OF_FLAGS:
                       return 0;
                       break;
              }
           } else {
               switch (*pStr) {
                   case 0x9:
                   case 0xA:
                   case 0xB:
                   case 0xC:
                   case 0xD:
                   case 0x24:
                   case 0x20:
                   case 0xA0:  //   Espacios en Blanco
                       if ((iData | NUMBER_HEX_FLAGS) == NUMBER_HEX_FLAGS)
                           iData = ((iData ^ NUMBER_HEX_ZERO) | NUMBER_OF_OK);
                       if ((iData & NUMBER_OF_FLAGS) == NUMBER_OF_OK)
                           iData |= NUMBER_OF_FINISH;
                       break;

                   case 0x0:    //  NULL Indica que se termina la cadena.
                       if ((iData & NUMBER_OF_FLAGS) == NUMBER_OF_FINISH)
                           return 0;
                       i = uiLn; //  Exit For.
                       break;

                   default:
                       return 0;
                       break;
               }
           }
       } else {
           if ((*pStr >= 0x30) && (*pStr <= 0x39)) {
               iData |= NUMBER_OF_OK;
               if ((iData & NUMBER_OF_FINISH) == NUMBER_OF_FINISH)
                   return 0;
               if ((iData & NUMBER_POW_FLAGS) == NUMBER_POW)
                   iData |= NUMBER_POW_FINISH;

           } else {
               switch ((int)*pStr) {
                   case 0x0:    //  NULL Indica que se termina la cadena.
                       if ((iData & NUMBER_POW_FLAGS) == NUMBER_POW)
                           return 0;
                       i = uiLn;    //  Exit For.
                       break;

                   case 0x2E:   //  "."  Solo 1
                       if (((iData & NUMBER_POW_FLAGS) == NUMBER_POW) ||
                          ((iData & NUMBER_OF_FINISH) == NUMBER_OF_FINISH) ||
                          ((iData & PUNTO_DECIMAL) == PUNTO_DECIMAL))
                           return 0;
                       iData |= PUNTO_DECIMAL;
                       break;

                   case 0x2B:
                   case 0x2D: //  "+|-" Solo 1
                       if ((iData & NUMBER_POW_FLAGS) == NUMBER_POW)
                           iData |= NUMBER_POW_FINISH;

                       else
                           if (((iData & NUMBER_OF_OK) == NUMBER_OF_OK) ||
                              ((iData & PUNTO_DECIMAL) == PUNTO_DECIMAL) ||
                              ((iData & SIGNO_SRC) == SIGNO_SRC))
                           return 0;

                       if ((iData & NUMBER_OF_FINISH) == NUMBER_OF_FINISH)
                           return 0;

                       iData |= SIGNO_SRC;
                       break;

                   case 0x2C:
                       if ( !((iData & NUMBER_OF_OK) == NUMBER_OF_OK) ||
                             ((iData & NUMBER_POW_FLAGS) == NUMBER_POW))
                           return 0;
                       break;

                   case 0x9:
                   case 0xA:
                   case 0xB:
                   case 0xC:
                   case 0xD:
                   case 0x24:  //  Solo se permiten al inicio de un Numero (Espacios en Blanco).
                       if (((iData & PUNTO_DECIMAL) == PUNTO_DECIMAL) ||
                          ((iData & NUMBER_OF_FINISH) == NUMBER_OF_FINISH) ||
                          ((iData & NUMBER_OF_OK) == NUMBER_OF_OK) ||
                          ((iData & NUMBER_POW_FLAGS) == NUMBER_POW))
                           return 0;
                       break;

                   case 0xA0:
                   case 0x20: //  Se permiten al Inicio/final de un numero.
                       if ((iData & NUMBER_OF_OK) == NUMBER_OF_OK)
                           iData |= NUMBER_OF_FINISH;
                       else
                           if (((iData & PUNTO_DECIMAL) == PUNTO_DECIMAL) ||
                              ((iData & NUMBER_POW_FLAGS) == NUMBER_POW))
                               return 0;
                       break;

                   case 0x26:   //  Es un Numero Hexadecimal
                       if (((iData & NUMBER_OF_FINISH) == NUMBER_OF_FINISH) ||
                          ((iData & NUMBER_OF_OK) == NUMBER_OF_OK) ||
                          ((iData & SIGNO_SRC) == SIGNO_SRC) ||
                          ((iData & PUNTO_DECIMAL) == PUNTO_DECIMAL) ||
                          ((iData & NUMBER_POW_FLAGS) == NUMBER_POW))
                           return 0;

                       pStr += iStep;

                       if ((*pStr == 0x48) || (*pStr == 0x68)) {
                           iData |= NUMBER_HEX;
                           iData ^= 0xFF000000;
                           i += iStep;
                       } else {
                           pStr -= iStep;
                       }
                       break;

                   case 0x44:
                   case 0x45:
                   case 0x64:
                   case 0x65:     //  Numeros en Formato ###e-###, ###e+###
                       if (((iData & NUMBER_OF_FINISH) == NUMBER_OF_FINISH) ||
                          ((iData & NUMBER_POW) == NUMBER_POW))
                           return 0;
                       if ((iData & NUMBER_OF_OK) == NUMBER_OF_OK) {
                           iData |= NUMBER_POW;
                           if ((iData & SIGNO_SRC) == SIGNO_SRC)
                               iData ^= SIGNO_SRC; //  Permitimos nuevamente los signos "+" y "-".
                       } else {
                           return 0;
                       }
                       break;

                   default:
                       return 0;
                       break;

               }   //  switch()
           }   //  if()
       }   //  if()
       pStr += iStep;
   }   //  For()

   switch (iData & NUMBER_OF_FLAGS) {
       case NUMBER_OF_OK:
       case NUMBER_OF_FLAGS:
           return 1;
           break;

       default:
           switch (iData & NUMBER_HEX_FLAGS) {
               case NUMBER_HEX_FLAGS:
                   return 1;
                   break;
           }
   }

   return 0;
}



main.h


#ifndef __MAIN_H__
#define __MAIN_H__

#include <windows.h>

#ifdef BUILD_DLL
   #define DLL_EXPORT __declspec(dllexport)
#else
   #define DLL_EXPORT __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C"
{
#endif

DLL_EXPORT int __stdcall IsNumericA (char*);
DLL_EXPORT int __stdcall IsNumericW (char*);
int IsNumericEx (char *pStr, size_t uiLn, int iStep);

#ifdef __cplusplus
}
#endif

#endif // __MAIN_H__



Dulces Lunas!¡.
The Dark Shadow is my passion.

rir3760

En el caso de C y si la intención es validar solo tipos enteros se puede utilizar la función strtol (prototipo en <stdlib.h>) dejando a esta la mayor parte del trabajo. Por ejemplo:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

#define NUM_CHARS 256

int validar_int(char const *linea, int *num);

int main(void)
{
   char linea[NUM_CHARS];
   int num;
   
   printf("Introduce un numero entero (signed int): ");
   fflush(stdout);
   if (fgets(linea, NUM_CHARS, stdin) == NULL)
      return EXIT_FAILURE;
   
   if (validar_int(linea, &num))
      printf("OK. El numero es %d\n", num);
   else
      puts("Error!");
   
   return EXIT_SUCCESS;
}


int validar_int(char const *linea, int *num)
{
   char *ch;
   long res;
   
   /* Descartamos el ultimo error y tratamos de convertir la linea */
   errno = 0;
   res = strtol(linea, &ch, 0);
   
   /* A continuacion verificamos ... */
   
   /* ... si al menos un caracter pudo convertirse con exito */
   if (res == 0 && ch == linea)
      return 0;
   
   /* ... si el numero esta en el rango valido (signed long) */
   if (errno == ERANGE && (res == LONG_MAX || res == LONG_MIN))
      return 0;
   
   /* ... si el resto de la linea es espacio blanco */
   while (isspace(*ch))
      ch++;
   if (*ch != '\0')
      return 0;
   
   /* ... si el numero esta en el rango valido (signed int) */
   if (res > INT_MAX || res < INT_MIN)
      return 0;
   
   /* Si todo se cumple el numero introducido es valido */
   *num = res;
   
   return 1;
}


Para números de tipo flotante se puede tomar una aproximación similar utilizando strtod (prototipo tambien en <stdlib.h>).

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

someRandomCode

O, existen todos estos:
int isalnum(int c);
int isalpha(int c);
int isascii(int c);
int isblank(int c);
int isdigit(int c);
int isxdigit(int c);

Que los podes encontrar en ctype.h

Dejo esos, y si lo que queres es validar una cadena:

int isnumeric(char * cadena) {
  while(*cadena)  { // Mismo que decir *cadena distinto de NULL o distinto de '\0'
    if(!isdigit(*cadena)) // Cuando lo que busquemos en la cadena NO sea un digito
      return 0; // Retornamoso 0 -> para decir falso, recordar que C no tiene tipo boolean, salvo que usen alguna API como la de Windows que tiene el tipo BOOL pero es una estructura y los valores se los interpreta como enteros al fin y al cabo
    cadena++; // Nos movemos al proximo en la cadena (incrementamos la direccion del puntero al proximo elemento)
  }
  return 1; // Si llegue hasta aca, es porque no me encontre con ningun caracter no-numerico, retorno 1-> pa' decir verdadero
}

BlackZeroX

#3
@rir3760.
@someRandomCode

Las funciones son buenas pero ineficientes, el caso es detectar una cadena con espacios validos y con formato valido, sin decir que debe ser RAPIDA:

* Numeros Enteros.
* Numeros con Punto decimal.
* Numeros con formato Hexadecimal de tamaño QWord.
* Numeros en NOTACION Cientifica.
* Las comas funcionan como separadores numericos de miles.
* En TODOS los casos anterior pueden ser tanto positivos como negativos.

Pasen a el post citado y veran una serie de Strings con una variedad amplia de formatos a probar en las funciones, ya que las vuestras se quedan a medias aun cuando son en C no superan en lo abosuluto ni siquiera a la que hice en vb6.

Dulces Lunas!¡.
The Dark Shadow is my passion.

someRandomCode

Hmm, no se, podrias aportar tiempos para las mismas?
No obstante, lo que propusiste tambien es una funcion, de tamaño un poco grandota.
Las funciones pueden ser ineficientes por todo lo que acarrea su invocacion, pero no te olvides que vivimos en una era donde un call o un ret no se consumen tantos ciclos como para morirse...

Ademas de que Visual Basic (aclaremos la version, ademas, porque varia) es bastante lento por tener que cargar de todo. Ubound y Lbound por ejemplo son funciones. Que estas usando ahi adentro.

Lo interesante seria que agarres y hagas un Time (GetSystemTimeMillis) antes y despues, y probeas un test case para demostrar que es cierto.

En general es buena idea no reinvetar la rueda, y tratar de usar cosas que ya han dado vueltas optimizadas por alguna gente que tiene el ojo en el tema.

Ojo, no estoy desmereciendo ni diciendo nada pero me da pereza bajarme VB, y hacer la prueba yo..

BlackZeroX

#5
.
Los test (tiempos) ya estan en la url... esta funcion trabaja a nivel Bit y es una traduccion mera de la que esta en vb6, tiene un tiempo de diferencia de 5-10 msc respectivamente a 1000 ciclos con varias strings a probar, lo unico en lo que radica esta verison con la de vb6, es que en VB6 no puedo trabajar directamente con cada caracter, y en esta si, en la de vb6 use RtlMoveMemory para tal cosa.

A esta version le saque dos variantes segun las string a probar es decir ANSI y UNICODE.

La funcion es grandota pero trabaja tan rapido como lo permiten las operaciones a nivel Bit y por punteros sin usar tanta memoria para las variables booleanas, en la de vb6 tomo partido de la longitud INT32 como es nativo de vb6 y de igual manera sacandole partido a las operaciones a nivel Bit...

En el caso de reinventar la rueda:
* Es cierto, pero si la funcion es lenta... o no cumple las espectativas, o mejor aun si no la puedes editar mejor recrearla, en este caso salio una funcion mas optimizada que la de vb6 y a su vez mas rapida, creo que reinventar la rueda en ocasiones tiene sus ventajas.

No soy el unico que hace este tipo de cosas: VBSpeed

Nota: No use en ningun momento LBound ni Ubound ya que no ocupo ningun tipo de array, estas funciones trabajan directamente con la estructura del SafeArray de un Array como es obvio...

Dulces Lunas!¡.
The Dark Shadow is my passion.

BlackZeroX

.
Los tiempos se han calculado con las APIS:

@kernel32
QueryPerformanceCounter
QueryPerformanceFrequency

Dulces Lunas!¡.
The Dark Shadow is my passion.

someRandomCode

Seria interesante que alguien los postee para no tener que hacerlo(como en realidad dije antes), je igual bien que si hicieron las pruebas, quede.
Y saber bien la metodologia que usaron para medirlo, pero esta bien, si usted lo dice felicitaciones atrasadas (porque hasta donde veo, es de hace raaato el thread :) )


BlackZeroX

#8
.
No te molesta si te dejo el EXE?, igual pongo una captura.

Archivos Fuente dll, y Compilados



La funcion isnumeric_Black esta en VB6, es la version en vb6 de la que esta aqui...

Dulces Lunas!¡.
The Dark Shadow is my passion.