strtol - úprava headera

Programovacie jazyky, rady, poradňa...
argeus
Novice
Novice
Príspevky: 5
Registrovaný: 15 apr 2011, 10:51

strtol - úprava headera

Príspevok od používateľa argeus »

Ahojte,
používam funkciu strtol na prevod int zo string-u. Problém je, že strtol v prípade, že nemôže konverziu uskutočniť vráti 0. To je celkom na nič, nakoľko 0 môže byť úplne parádny znak ktorý chcem skonvertovať, a teda následne nemožno odlíšiť vstup s chybnou hodnotou a nulou. Ideálne by bolo prinútiť strtol vracať inú hodnotu. Snažil som sa to urobiť tak, že som do kódu zaradil header s definíciou strtol-u a snažil sa zmeniť hodnotu return-u úplne na konci headera. Čo som testoval, kladné hodnoty vracia podľa želania, záporné +/- okolo mínus 10 OK, pri iných záporných hodnotách program padá. Skutočne si nemyslím, že je na vine môj program, takmer určite je na vine header. Chcel by som z neho v prípade neúspešnej konverzie dostať -1. Aj mínus -10 by bolo OK, ibaže keď to takto blbne, tak tomu neverím a chcem vedieť prečo. Napadá niekoho dôvod prečo by header nemohol vracať ľubovoľnú hodnotu? Trápim sa s tým už celý deň, za akúkoľvek radu budem vďačný.

Kód: Vybrať všetko

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#if HAVE_LIMITS_H
#include <limits.h>
#endif
#ifndef ULONG_MAX
#define	LONG_MAX (~(1 << (sizeof (long) * 8 - 1)))
#define LONG_MIN (-LONG_MAX-1)
#define ULONG_MAX ((unsigned long) ~(unsigned long) 0)
#endif

#if STDC_HEADERS
#include <stddef.h>
#include <stdlib.h>
#else
extern int errno;
#endif

#ifndef	UNSIGNED
#define	UNSIGNED	0
#endif

/* Convert NPTR to an `unsigned long int' or `long int' in base BASE.
   If BASE is 0 the base is determined by the presence of a leading
   zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal.
   If BASE is < 2 or > 36, it is reset to 10.
   If ENDPTR is not NULL, a pointer to the character after the last
   one converted is stored in *ENDPTR.  */
#if	UNSIGNED
unsigned long int
#define	strtol
#else
long int
#endif
demizon (nptr, endptr, base)
     const char *nptr;
     char **endptr;
     int base;
{
  int negative;
  register unsigned long int cutoff;
  register unsigned int cutlim;
  register unsigned long int i;
  register const char *s;
  register unsigned char c;
  const char *save;
  int overflow;
  
  if (base < 0 || base == 1 || base > 36)
    base = 10;

  s = nptr;

  /* Skip white space.  */
  while (isspace (*s))
    ++s;
  if (*s == '\0')
    goto noconv;

  /* Check for a sign.  */
  if (*s == '-')
    {
      negative = 1;
      ++s;
    }
  else if (*s == '+')
    {
      negative = 0;
      ++s;
    }
  else
    negative = 0;

  if (base == 16 && s[0] == '0' && toupper (s[1]) == 'X')
    s += 2;

  /* If BASE is zero, figure it out ourselves.  */
  if (base == 0)
    {
      if (*s == '0')
	{
	  if (toupper (s[1]) == 'X')
	    {
	      s += 2;
	      base = 16;
	    }
	  else
	    base = 8;
	}
      else
	base = 10;
    }

  /* Save the pointer so we can check later if anything happened.  */
  save = s;

  cutoff = ULONG_MAX / (unsigned long int) base;
  cutlim = ULONG_MAX % (unsigned long int) base;

  overflow = 0;
  i = 0;
  for (c = *s; c != '\0'; c = *++s)
    {
      if (isdigit (c))
	c -= '0';
      else if (isalpha (c))
	c = toupper (c) - 'A' + 10;
      else
	break;
      if (c >= base)
	break;
      /* Check for overflow.  */
      if (i > cutoff || (i == cutoff && c > cutlim))
	overflow = 1;
      else
	{
	  i *= (unsigned long int) base;
	  i += c;
	}
    }

  /* Check if anything actually happened.  */
  if (s == save)
    goto noconv;

  /* Store in ENDPTR the address of one character
     past the last character we converted.  */
  if (endptr != NULL)
    *endptr = (char *) s;

#if	!UNSIGNED
  /* Check for a value that is within the range of
     `unsigned long int', but outside the range of `long int'.  */
  if (i > (negative ?
	   -(unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX))
    overflow = 1;
#endif

  if (overflow)
    {
      errno = ERANGE;
#if	UNSIGNED
      return ULONG_MAX;
#else
      return negative ? LONG_MIN : LONG_MAX;
#endif
    }

  /* Return the result of the appropriate sign.  */
  return (negative ? -i : i);

noconv:;
  /* There was no number to convert.  */
  if (endptr != NULL)
    *endptr = (char *) nptr;
  return 0;
}
pcsiete
Medium Star
Medium Star
Príspevky: 413
Registrovaný: 07 dec 2012, 18:47

Re: strtol - úprava headera

Príspevok od používateľa pcsiete »

Ahojte,
používam funkciu strtol na prevod int zo string-u. Problém je, že strtol v prípade, že nemôže konverziu uskutočniť vráti 0. To je celkom na nič, nakoľko 0 môže byť úplne parádny znak ktorý chcem skonvertovať, a teda následne nemožno odlíšiť vstup s chybnou hodnotou a nulou. Ideálne by bolo prinútiť strtol vracať inú hodnotu. Snažil som sa to urobiť tak, že som do kódu zaradil header s definíciou strtol-u a snažil sa zmeniť hodnotu return-u úplne na konci headera. Čo som testoval, kladné hodnoty vracia podľa želania, záporné +/- okolo mínus 10 OK, pri iných záporných hodnotách program padá. Skutočne si nemyslím, že je na vine môj program, takmer určite je na vine header. Chcel by som z neho v prípade neúspešnej konverzie dostať -1. Aj mínus -10 by bolo OK, ibaže keď to takto blbne, tak tomu neverím a chcem vedieť prečo. Napadá niekoho dôvod prečo by header nemohol vracať ľubovoľnú hodnotu? Trápim sa s tým už celý deň, za akúkoľvek radu budem vďačný.
Čo sa snažíš dosiahnuť? Tvoj problém sa dá vyriešiť jednoducho kontrolovaním hodnoty druhého parametru, teda pointrom na znak za poslednou platnou cifrou.

Kód: Vybrať všetko

int zaklad = ...;
char * str = ...;
char * endptr = NULL;

long int val = strtol(str, &endptr, zaklad);
if(val==(long int)0)
	if(endptr==str)
		//neúspešné
	else
		//úspešné
Samozrejme, situáciu komplikujú prefixy 0 a 0x (ak zaklad=0), pretože pokým nie je táto predpona nasledovaná cifrou platnou pre danú sústavu, tak sa nula na začiatku berie ako platná cifra a nasledujúce znaky sa ignorujú. Kvôli tomu sa neoplatí používať základ 0, hoci pre konkrétne základy sú pre nich špecifické predpony nepovinné.
argeus
Novice
Novice
Príspevky: 5
Registrovaný: 15 apr 2011, 10:51

Re: strtol - úprava headera

Príspevok od používateľa argeus »

Presne na toto odporúčanie som narazil, ale nevedel som to rozbehať. Môj zápis v programe vyzerá nejako takto (je to len príklad z webu, ale celkom sedí), viď nižšie. Môj základ čísla je vždy 10, čiže žiadna divočina. Som z toho jeleň ako použiť podmienku if(endptr==str). To mám potom porovnávať szNumbers==pEND alebo pEnd==pEnd alebo?? A inak, díki za odpoveď.

Kód: Vybrať všetko

* strtol example */
#include <stdio.h>      /* printf */
#include <stdlib.h>     /* strtol */

int main ()
{
  char szNumbers[] = "2001 60c0c0 -1101110100110100100000 0x6fffff";
  char * pEnd;
  long int li1, li2, li3, li4;
  li1 = strtol (szNumbers,&pEnd,10);
  li2 = strtol (pEnd,&pEnd,16);
  li3 = strtol (pEnd,&pEnd,2);
  li4 = strtol (pEnd,NULL,0);
  printf ("The decimal equivalents are: %ld, %ld, %ld and %ld.\n", li1, li2, li3, li4);
  return 0;
}
pcsiete
Medium Star
Medium Star
Príspevky: 413
Registrovaný: 07 dec 2012, 18:47

Re: strtol - úprava headera

Príspevok od používateľa pcsiete »

V tom kóde sa predpokladá, že nenastane sitácia v ktorej by bol vstup neplatný. Tým pádom si nikde nenechávajú uloženú starú hodnotu začiatku nového reťazca. Ten príklad hore sa dá s overovaním prepísať ako:

Kód: Vybrať všetko

int main ()
{
	char szNumbers[] = "2001 2002 1978 1992 12000000 neplatne"; //len v desiatkovej sústave
	char * stringStart = NULL;
	char * stringEnd = szNumbers;
	  
	int index = 0;
	long int cisla[6];
	do
	{
		/* vždy na začiatku cyklu nastav nový začiatok na predošlý koniec reťazca */
		stringStart = stringEnd;
		
		/* ...ale nový koniec ešte nepoznáme...*/
		stringEnd = NULL;
		
		/* prejdi časť reťazca po prvý nečíselný znak, ulož vrátenú hodnotu do arrayu a zmeň koniec reťazca */
		cisla[index] = strtol(stringStart, &stringEnd, 10);
	} 
	/* ak sa začiatok reťazca rovná miestu kde našla funkcia neplatný znak,
	 * znamená to, že hneď začiatok bol neplatný a treba skončiť */
	while(cisla[index++]!=NULL || cisla[index-1]==NULL && stringStart!=stringEnd);
	
	/* 
		na toto miesto sa kód dopracuje iba v prípade, že platila podmienka:
			cisla[posledneVrateneCislo] == 0 && zaciatokRetazca==koniecRetazca 	
	*/

	return 0;
}
harrison314
Hardcore addict
Hardcore addict
Používateľov profilový obrázok
Príspevky: 8219
Registrovaný: 27 máj 2009, 20:42
Bydlisko: Bratislava
Kontaktovať používateľa:

Re: strtol - úprava headera

Príspevok od používateľa harrison314 »

Je to z brucha, ale co tak pouzit sscanf?

Kód: Vybrať všetko

int readInt(char *str,int *value)
{
  if(sscanf(str,"%x",value)==1) return 1; //pre hexa
  if(sscanf(str,"%o",value)==1) return 1;// pre osmickove cislo
  if(sscanf(str,"%d",value)==1) return 1; // pre desiatkove cislo
 return 0; //neporrilo sa nacitat cislo
}
pcsiete
Medium Star
Medium Star
Príspevky: 413
Registrovaný: 07 dec 2012, 18:47

Re: strtol - úprava headera

Príspevok od používateľa pcsiete »

Niekoľko rozdielov tam je. Obidve funkcie patria do jednej "rodiny", no strtol je rýchlejší, a čo sa týka spracovania vstupu, strtol vracia vždy najdlhšiu možnú sekvenciu znakov podľa nastavenia. Kvôli svojmu druhému parametru je taktiež oveľa výhodnejší pri spracovaní dlhších číslených sekvencií.
argeus
Novice
Novice
Príspevky: 5
Registrovaný: 15 apr 2011, 10:51

Re: strtol - úprava headera

Príspevok od používateľa argeus »

Chlapi, ďakujem. Už som v obraze.
hneď ako zase budem mať trochu času tak to otestujem.
Ešte raz vďaka.
Napísať odpoveď