Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: C-Rivien lukeminen

Sivun loppuun

tunkio [14.03.2011 08:15:39]

#

Morjens,

Mitenkähän onnistuu "helposti" C:llä rivien lukeminen/kirjoittaminen tekstitiedostosta. Jos ajatellaan että on tiedosto jossa on 2 riviä ja haluaisin muokata toista riviä vaan. Eli siis kirjoitettaisiin vain 2 rivin päälle uusi tieto. Olen sitä yrittänyt jotenki näin tehdä mutta ei lähde oikein homma skulaan.

fgets(mjono1, sizeof mjono1,tiedosto);
fputs(mjono2,tiedosto);

Eli siis luettas eka rivi, ja sitten tallennettas toiselle rivin päälle uusi tieto mutta ei oikein pelitä. Ja toiselle riville tuleva tietotyyppi on char jonka koko on max 300.

Teuro [14.03.2011 08:27:47]

#

Lue ensin koko tiedosto muistiin johonkin tietorakenteeseen. Ja sitten muokkaat oikeaa kohtaa ja tallennat ýhtenä limppuna takaisin.

tunkio [14.03.2011 08:58:13]

#

Onko ajatuksia minkälaiseen tietorakenteeseen se kannattas lukea? Eka rivillä ei ole kuin max 5 merkkiä mutta sitten toisella rivillä voi olla se max 300 merkkiä. Millä sitten "handlaan" sen että muokkaan vain toista riviä?

Teuro [14.03.2011 09:50:48]

#

No yksi tapa on siirtyä c++:n jossa tulisi melko iso kasa valmista kamaa mukana. Mutta kuitenkin tee vaikka tietue (struct), jossa on ainoana muuttujana char[301] rivi. Sitten teet taulukon, jonka tyyppi ei ole primitiivi, vaan tuon äsken luotu tietue. Seuraavaksi muutat taulukon toista riviä ja tallennat rivi kerrallaan takaisin tiedostoon.

tunkio [14.03.2011 10:24:02]

#

Eli meinaat että tekisin esim näin.

struct rivi{
            char[301];
};

struct rivi taulukko[];

Tämän jälkeen sitten lukisin tiedostosta rivi kerrallaan tiedot taulukkoon ja muuttaisin vain toista riviä? Tekeekö tuo nyt taulukon joka rivista tuon 301 merkkiä pitkän?

Chiman [14.03.2011 10:29:42]

#

tunkio kirjoitti:

Onko ajatuksia minkälaiseen tietorakenteeseen se kannattas lukea? Eka rivillä ei ole kuin max 5 merkkiä mutta sitten toisella rivillä voi olla se max 300 merkkiä. Millä sitten "handlaan" sen että muokkaan vain toista riviä?

Lue yksittäiseen char-taulukkoon tiedoston sisältö, etsi ensimmäisen rivinvaihtomerkin sijainti, kirjoita uusi sisältö sen jälkeen ja tallenna taulukon sisältö aiemman tiedoston päälle.

Torgo [14.03.2011 11:56:15]

#

Taulukon voit tehdä vaikkapa näin:

File * tiedosto;
size_t koko;
char * sisalto;

tiedosto = fopen("tiedosto.txt", "r+");
if(tiedosto == NULL) {
    exit(EXIT_FAILURE);
}

// Varaa taulukko
fseek(tiedosto, 0, SEEK_END);
koko = ftell(tiedosto);
rewind(tiedosto);
sisalto = malloc(koko);
if(sisalto == NULL) {
    fclose(tiedosto);
    exit(EXIT_FAILURE);
}

// Tee kaikki muu.

free(sisalto);

Ajatuksessa muokata taulukosta yksittäistä riviä on se ongelma, että jos uusi rivi on vanhaa pidempi, niin taulukolle pitäisi varata lisää tilaa ja siirtää loppua eteenpäin. Vastaavasti jos rivi on lyhyempi, niin tyhjä tila pitäisi saada jollain täytettyä. Kaikkein helpointa on olla muokkaamatta taulukkoa lainkaan. Eli kirjoitetaan vain talukosta tiedostoon rivi kerrallaan. Sitten kun tullaan muokattavalle riville, niin taulukossa hypätään kokonaan sen yli ja kirjoitetaan suoraan tiedostoon haluttu uusi rivi.

tunkio [14.03.2011 12:18:01]

#

Ok, pitääpä testata tuota tänään illemmalla mikäli sais sen toimimaan.
Miten sitten onnistuisi se helpoiten että kun luen tuon taulukon sisältöä niin ensimmäinen rivi jätetään koskemattomaksi ja toiselle riville sitten kirjoittaisin uuden tiedon vanhan päälle?
Ja myös miten kirjoitan tuon taulukon takaisin tiedostoon sitte rivi kerrallaan?

Torgo [14.03.2011 12:27:49]

#

Lue ensin koko tiedosto taulukkoon. Jos avaat tiedoston r/w moodiin, niin flushaa se välillä. Toinen vaihtoehto on lukea se ensin lukumoodissa ja sitten avata uudestaan kirjoitusmoodiin.
Sitten splittaa taulukko riveihin vaikkapa strtok -funktiolla. Varo yli-indeksointia.
Tarkista rivi. Muokkaa tarvittaessa. Kirjoita tiedostoon. Toista kunnes rivit loppuvat.

tunkio [14.03.2011 21:59:09]

#

No niin, saihan sitä taas hyvin kulutettua aikaan tämän parissa :) Eli ei nyt ihan menny niinku piti tuo ohjelma. Tällä hetkellä koodi näyttää tältä

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

int main () {
  FILE * pFile;
  long lSize;
  char * buffer;


  pFile = fopen ( "tiedosto.txt" , "r+" );
  if (pFile==NULL) {fputs ("File error",stderr);system("pause"); exit (1);}

  fseek (pFile , 0 , SEEK_END);
  lSize = ftell (pFile);
  rewind (pFile);

  buffer = (char*) malloc (sizeof(char)*lSize);

  fread (buffer,1,lSize,pFile);

   char * rivi1;
   char * rivi2;
   rivi1 = strtok(buffer,"\n");
   rivi2  = strtok(NULL,"\n");
   printf("\n%s", rivi1);
   printf("\n%s", rivi2);

   rivi2 = "Vaihdettiin infoa";
   printf("\n%s\n", rivi2);

   fputs(rivi1,pFile);
   fputs(rivi2,pFile);

  fclose (pFile);
  free (buffer);
  system("pause");
  return 0;
}

Eli aluksi tiedostossa on seuraavat rivit:
Keijo
120000

Ohjelma tulostaa ensimmäisellä ajokerralla seuraavan setin:
keijo
120000ogra&#9573;
Vaihdettiin infoa

Ja kun sitten vielä seuraavan kerran ajan ohjelman niin se tulostaa tämän:
keijo
120000keijoVaihdettiin infoaes\Rox&#9575;
Vaihdettiin infoa

Mistä nuoylimääräiset merkit tulee tuohon ekaan ajokertaan?
Ja en oikein oo varmaan tajunnu vielä tuota että miten saan sitten kirjoitettua takasin rivit tiedostoon jos vähän muuttelen toka riviä?

Torgo [14.03.2011 23:50:03]

#

Ainakin sinulta puuttuu includeista string.h, mistä löytyy strtok.
Lisäksi tiedosto on kelattava alkuun (rewind) ja virta tyhjennettävä (fflush) lukemisen jälkeen. TAI: sulje tiedosto lukemisen jälkeen ja avaa se uudestaan kirjoitustilaan.
Kolmanneksi, strtok korvaa rivinvaihdot nullilla, joten ne on kirjoitettava tiedostoon uudestaan:

fprintf(pFile, "%s\n", rivi1);

Ja vielä neljänneksi, rivi2 osoittaa nyt taulukkoon. Jos tuolla olisi vielä kolmas rivi, niin kirjoittaisit sen päälle. Nykyisellään kirjoitat alueelle mistä ei ole varattu tilaa. Et tarvitse tuota rivi2 -osoitinta mihinkään vain voit kirjoittaa sen rivin suoraan tiedostoon:

fprintf(pFile, "Vaihdettiin infoa\n");

Metabolix [15.03.2011 01:18:53]

#

Teinpä ihan mielenkiinnosta funktion, jolla voi korvata yhden rivin tiedostosta.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int korvaa(const char* tiedosto, int rivinumero, const char* rivi) {
	FILE* f = 0;
	char* muisti = 0;
	int koko, kohta;
	int ret = 0;

	/* Avataan tiedosto luettavaksi. */
	f = fopen(tiedosto, "r");
	if (!f) {
		ret = 1;
		goto loppu;
	}

	/* Lasketaan datan enimmäismäärä. */
	fseek(f, 0, SEEK_END);
	koko = ftell(f) + strlen(rivi) + 1;
	fseek(f, 0, SEEK_SET);

	/* Varataan tiedoston verran muistia. */
	muisti = malloc(koko);
	if (!muisti) {
		ret = 2;
		goto loppu;
	}

	/* Luetaan tiedoston alusta rivejä muistiin. */
	kohta = 0;
	while (rivinumero > 0) {
		--rivinumero;
		fgets(muisti + kohta, koko - kohta, f);
		if (feof(f)) {
			ret = 3;
			goto loppu;
		}
		kohta = ftell(f);
	}

	/* Luetaan vanhan rivin sisältö ja laitetaan tilalle uusi rivi. */
	fgets(muisti + kohta, koko - kohta, f);
	strcpy(muisti + kohta, rivi);
	kohta += strlen(rivi);
	muisti[kohta] = '\n';
	++kohta;

	/* Luetaan loppu tiedosto muistiin. */
	kohta += fread(muisti + kohta, 1, koko - kohta, f);
	fclose(f);

	/* Kirjoitetaan koko data kerralla takaisin tiedostoon. */
	f = fopen(tiedosto, "w");
	if (!f) {
		ret = 4;
		goto loppu;
	}
	fwrite(muisti, kohta, 1, f);

	ret = 0;
loppu:
	if (muisti) {
		free(muisti);
	}
	if (f) {
		fclose(f);
	}
	return ret;
}

Esimerkiksi seuraava funktiokutsu korvaa tiedostosta "koe.txt" ensimmäisen rivin tekstillä "ABC":

korvaa("koe.txt", 0, "ABC");

Tuossa alkuperäisessä tapauksessa, jossa rivejä on kaksi ja korvattavana on jälkimmäinen, riittää paljon helpompi koodi:

FILE* f;
char apu[1024]; // Riittävästi; jos et tiedä, käytä tuota ftell-temppua.
char uusirivi[] = "abcdefg\n";

f = fopen("tiedosto.txt", "r");
fgets(apu, sizeof(apu), f);
fclose(f);

f = fopen("tiedosto.txt", "w");
fprintf(f, "%s%s", apu, uusirivi);
fclose(f);

tunkio [15.03.2011 12:31:58]

#

Jes, näyttäs nytten toimivan tuo homma ihan ok mitä äkkiä testailin.
Kiitoksia auttaneille, palailen asiaan mikäli lisää ongelmia ilmaantuu :)

tunkio [20.03.2011 19:53:45]

#

Moikka taas,

Nyt onki sitten jännä ongelma.
Eli kun ajan ohjelman normaalisti läpi, tulostaa tulokseksi ihan mitä sattuu.
Mutta kun taas ajan sitten läpi Debuggaamalla siten että menen askel kerrallaan tuossa add funktiossa niin tulostus toimiikin ihan oikein.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define KOKO 300

void reverse( char * );
void add( char *, char *, char * );
void reset( char *, int );
void empty( char *, int );

int main () {
   FILE * pFile;
   long lSize;
   char * buffer;

   pFile = fopen ( "tiedosto.txt" , "r+" );
   if (pFile==NULL) {fputs ("File error",stderr);system("pause"); exit (1);}

   fseek (pFile , 0 , SEEK_END);
   lSize = ftell (pFile);
   rewind (pFile);

   buffer = (char*) malloc (sizeof(char)*lSize);

   fread (buffer,1,lSize,pFile);

   rewind(pFile);
   fflush(pFile);

   char * rivi1;
   char * rivi2;
   rivi1 = strtok(buffer,"\n");
   rivi2  = strtok(NULL,"\n");
   printf("\n%s", rivi1);
   printf("\n%s", rivi2);

   char numero[KOKO];
   char summa[KOKO+1];

   reset(numero, KOKO);

   empty(summa, KOKO+1);


   printf("\nAnna toinen luku: ");

   fgets(numero,KOKO,stdin);
   if(numero[strlen(numero)-1] == '\n')
      numero[strlen(numero)-1] = '\0';
   else
      while(getc(stdin) != '\n');

   add(rivi2,numero,summa);
   printf("\n\n%s",summa);

   fprintf(pFile, "%s\n", rivi1);
   fprintf(pFile, "%s\n", summa);
   fclose (pFile);
   free (buffer);

    system("pause");
   return 0;
}

void reverse( char * numero ){
  int pituus = strlen(numero);
  char * kopio = (char *)malloc(( pituus + 1) * sizeof(char));
  int i;

  for(i=0; i < pituus; i++){
    kopio[i] = numero[pituus-1 -i];
  }
  kopio[i] = '\n';

  for(i=0; i < pituus; i++)
    numero[i] = kopio[i];
  free(kopio );
}

void add( char * num1, char * num2, char * num3 ){

   //printf("\nLuku1:%s", num1);
   //printf("\nLuku2:%s", num2);
   //printf("\nSumma:%s", num3);
   int i, j;
   int tulos = 0;
   int muistinumero = 0;
   int apu1, apu2;
   int pituus = 0;

   reverse(num1);
   reverse(num2);

   //printf("\nLuku1:%s", num1);
   //printf("\nLuku2:%s", num2);

   if( strlen(num1) >= strlen(num2))
     pituus = strlen(num1);
   else
     pituus = strlen(num2);

   //printf("\nPituus: %d",pituus);

   for(i=0, j=0; i < pituus; i++,j++){
      tulos = 0;

      apu1=  (int)(num1[i] - '0');
      if( apu1 < 0)
         apu1 = 0;
      //printf("\nApu1:%d", apu1);

      apu2 = (int)(num2[j] - '0');
      if( apu2 < 0 )
         apu2 = 0;
      //printf("\nApu2:%d", apu2);

      tulos = apu1 + apu2 + muistinumero;

      printf("\nTulos:%d", tulos);

      muistinumero = 0;

      if( tulos > 9 ){
 	    muistinumero = tulos / 10;
	     tulos = tulos % 10;
      }
      num3[i] = (char)(tulos + '0');

   }// for looppi

   if( muistinumero > 0)
     num3[i] = (char)(muistinumero + '0');

   reverse(num1);
   reverse(num2);
   reverse(num3);
}


void reset( char * number, int lSize ){

   int i;
   for(i=0; i < lSize; i++){
     number[i] = '0';
   }
}

void empty( char * number, int lSize){

   int i;
   for(i=0; i < lSize; i++)
     number[i] = '\0';

}

Onko mitään hajua mikä tuon voisi aiheittaa? Vai onko tuossa ohjelmassa jotakin mätää. Tuntuu että alkaa koneenkin tuuletin huutamaan miljoonaa ku alkaa suorittamaan tuota ohjelmaa.

EDIT: Niin ja käytän tuota Dev C++ IDE :ä noitten kääntämiseen. Houmasin tuossa että prosessorin tehoja menee noin puolet tuohon kun alan leikkiin tuon koodinpätkän kanssa. Heti kun sammuttaa tuon Dev-C++ niin johan loppuu hurinat.

Lisättäköön vielä että pienillä numeroilla kuten esim 100 toimii ihan ookoo mutta kun pistetään esim 123456789 niin menee ihan sekaisin.

tunkio [21.03.2011 13:23:47]

#

Joo näyttää että tuo vetää jotenkin ihan kuralle koko prosessin. Tuossa ku aloin taas kokeileen tuota koodinpätkää debuggaamalla läpi tuon add - funktion niin noin puolessa välissä alko kone taas huutamaan "hoosiannaa". Vetääkö tuossa koodissa jokin tosiaan koneen tehot "nuppiin" ?

jalski [21.03.2011 19:53:35]

#

Kerropas, mitä tarkalleen ottaen olet toteuttamassa ja laitatko tuosta oikeanlaisesta syötetiedostosta jonkunlaisen esimerkin myös esille.

Jos innostun, niin voin kokeilla toteuttaa tuosta toimivan version Open Watcom kääntäjällä.

Voin myös näyttää, miten paljon helpompaa on valita tuollaiseen työhön ohjelmointityökalu, jossa on kunnolliset merkkijonojen käsittely ominaisuudet, mikä kykenee laskemaan tarkasti suoraan merkkijonoilla ja jossa on myös kunnolliset virheenkäsittelyominaisuudet.

Metabolix [21.03.2011 20:48:40]

#

Funktion reset voi korvata standardikirjaston strcpy-funktiolla (merkkijono "0" riittää nollaksi). Funktion empty voisi korvata standardikirjaston memset-funktiolla tai poistaa kokonaan, koska tuloshan ylikirjoitetaan kuitenkin. Funktio reverse on huonosti toteutettu, oikeasti tarvitset vain yhden silmukan etkä lainkaan tuota toista taulukkoa.

Miksi add-funktion for-silmukassa on kaksi laskuria, joilla on kuitenkin aina sama arvo? Entä mitä tapahtuu, kun tiedostosta luetussa luvussa on 3 merkkiä ja käyttäjän syöttämässä luvussa on vaikka 100 merkkiä? (Vinkki: paljonko muistia olet silloin varannut mallocilla ja mikä for-silmukan laskurin arvo on suurimmillaan?) Miksi käyttäjän syöte luetaan fgets-funktiolla eikä esimerkiksi scanf-funktiolla %s-formaatilla tai jopa scansetin avulla?

char luku[300];
scanf("%299[0123456789]", luku);

Rivinvaihtomerkit kuuluu laittaa rivien loppuun eikä alkuun. Windows-käyttäjänä asia voi olla vaikea tajuta, mutta tältä se virhe useimmissa käyttöjärjestelmissä näyttää käytännössä:

komento> ./testi

Hello, World!komento>

Kun rivinvaihto laitetaan oikeaan paikkaan, tulos on tällainen:

komento> ./testi
Hello, World!
komento>

tunkio [21.03.2011 21:34:06]

#

jalski kirjoitti:

Kerropas, mitä tarkalleen ottaen olet toteuttamassa ja laitatko tuosta oikeanlaisesta syötetiedostosta jonkunlaisen esimerkin myös esille.

Jos innostun, niin voin kokeilla toteuttaa tuosta toimivan version Open Watcom kääntäjällä.

Voin myös näyttää, miten paljon helpompaa on valita tuollaiseen työhön ohjelmointityökalu, jossa on kunnolliset merkkijonojen käsittely ominaisuudet, mikä kykenee laskemaan tarkasti suoraan merkkijonoilla ja jossa on myös kunnolliset virheenkäsittelyominaisuudet.

Elikkäs, on tiedosto jossa on kaksi riviä. Esim
Keijo
11111


Tiedostosta täytyisi lukea molemmat rivit. Molemmat tulisi säiliä esim char muuttujaan.
Tiedoston toiseen riviin lisätään sitten numero (voi olla max 300 merkkiä pitkä) joka kysytään itse ohjelmassa.
Eli jos annan vaikka luvun 123456789 tulisi silloin tiedostoon olla seuraavanlainen:
Keijo
123467900

P.S Toivottavasti innostut :)

MetaBolix kirjoitti:

Funktion reset voi korvata standardikirjaston strcpy-funktiolla (merkkijono "0" riittää nollaksi). Funktion empty voisi korvata standardikirjaston memset-funktiolla tai poistaa kokonaan, koska tuloshan ylikirjoitetaan kuitenkin. Funktio reverse on huonosti toteutettu, oikeasti tarvitset vain yhden silmukan etkä lainkaan tuota toista taulukkoa.

Ok, otin nuo turhat reset ja empty funktiot tuolta pois. Samalla korvasin sen strcpy jonka mainitsitkin tuossa. Samalla otin reversestä pois toisen silmukan ja lisäsin sinne kans strcpy.

MetaBolix kirjoitti:

Miksi add-funktion for-silmukassa on kaksi laskuria, joilla on kuitenkin aina sama arvo? Entä mitä tapahtuu, kun tiedostosta luetussa luvussa on 3 merkkiä ja käyttäjän syöttämässä luvussa on vaikka 100 merkkiä? (Vinkki: paljonko muistia olet silloin varannut mallocilla ja mikä for-silmukan laskurin arvo on suurimmillaan?) Miksi käyttäjän syöte luetaan fgets-funktiolla eikä esimerkiksi scanf-funktiolla %s-formaatilla tai jopa scansetin avulla?

No niin tämäpä siinä onkin, että kun ei oikein mene jakeluun tuo koodi nytten. Alkaa oleen sen verran nuppi sekasin :) Tässähän se varmaan ongelman ydin onkin että mitä jos käyttäjän antama numero onkin sen 160 merkkiä ja tiedostossa oleva 3 merkkiä.

Tuon reversen tein nytten näin

void reverse( char * numero ){
  int pituus = strlen(numero);
  char * kopio = (char *)malloc(( pituus + 1) * sizeof(char));
  int i;

  for(i=0; i < pituus; i++){
    kopio[i] = numero[pituus-1 -i];
  }

   strcpy(numero,kopio);
  free(kopio );
}

(Mod. korjasi tagin.)

tunkio [21.03.2011 22:38:25]

#

Oho, tais mennä tuo tunti umpeen ku aloin tuota katseleen. Pahoitteluni tästä kun näyttää vähän sekavalta. Eli tuo reverse on nyt tuon näköinen. Näyttää siltä kun käänään numerot niin toisen numeron joka on kysytty käyttäjältä tulee jotain ihme roskaa mukaan? Ttulee ylimääräisiä merkkejä mukaan.

Metabolix [21.03.2011 23:00:12]

#

Muistatko, mitä merkkijonon lopussa pitäisi aina olla ja mitä kopio-merkkijonosi nyt ei sisällä? Sitä paitsi sanoin jo, että et tarvitse aputaulukkoa reversen toteutukseen. Voit silmukassa kopioinnin sijaan vaihtaa merkin vastaavaan merkkiin tekstin vastakkaisesta päästä. (Silmukassa pitää tällöin käydä läpi vain puolet tekstistä.)

add-funktiosta: Piirrä ruutupaperille oikeanlaiset taulukot ja katso siinä, mistä "ruudusta" merkki haetaan for-silmukan kuluessa. Pitäisi kyllä huomata, että lyhyemmästä taulukosta mennään yli.

tunkio [22.03.2011 08:26:02]

#

Metabolix kirjoitti:

Muistatko, mitä merkkijonon lopussa pitäisi aina olla ja mitä kopio-merkkijonosi nyt ei sisällä? Sitä paitsi sanoin jo, että et tarvitse aputaulukkoa reversen toteutukseen. Voit silmukassa kopioinnin sijaan vaihtaa merkin vastaavaan merkkiin tekstin vastakkaisesta päästä. (Silmukassa pitää tällöin käydä läpi vain puolet tekstistä.)

add-funktiosta: Piirrä ruutupaperille oikeanlaiset taulukot ja katso siinä, mistä "ruudusta" merkki haetaan for-silmukan kuluessa. Pitäisi kyllä huomata, että lyhyemmästä taulukosta mennään yli.

Anteeksi tyhmyyteni tämän C-kielen kanssa :)

Merkkijonon lopussahan on ilmeisesdti hyvä olla '\0'. En oikein tajua miten tuo hoidetaan yhdellä silmukalla ilman aputaulukkoa?

add-funktiosta: Joo kyllä hokasin sen että ylihän siinä mennään toisesta loopista mutta eikös ton pitäisi juuri asettaa arvo 0 sille kohin koska sen arvo on alle 0 mitä debuggaamalla huomasin.

Mutta jännä hommahan tässä on että mikäli teen ohjelman jossa kysytään käyttäjältä kaks numeroa näppäimistöltä niin ohjelma toimii moitteetta. Mutta kun lisätään nämä tiedostosta haut niin homma menee reisille.

jalski [22.03.2011 09:09:15]

#

tunkio kirjoitti:

Elikkäs, on tiedosto jossa on kaksi riviä. Esim
Keijo
11111

Tiedostosta täytyisi lukea molemmat rivit. Molemmat tulisi säiliä esim char muuttujaan.
Tiedoston toiseen riviin lisätään sitten numero (voi olla max 300 merkkiä pitkä) joka kysytään itse ohjelmassa.
Eli jos annan vaikka luvun 123456789 tulisi silloin tiedostoon olla seuraavanlainen:
Keijo
123467900

Onko muuten oikeasti tarvetta noin järjettömän isoille luvuille (max. 300 merkkiä)? Tuo ASCII-decimal lukujen yhteenlaskeminen onnistuisi nimittäin kätevästi fpu:n avulla BCD-luvuilla 18 decimaalinumeron mittaisiin lukuihin asti.

Mitä tulee tuohon add-funktioon ja silmukan ylimenoon. Ainahan saat noista yhteenlaskettavista luvuista samanmittaisia merkkijonoja lisäämällä lyhyemmän merkkijonon eteen oikean määrän etunollia.

tunkio [22.03.2011 11:01:27]

#

jalski kirjoitti:

Onko muuten oikeasti tarvetta noin järjettömän isoille luvuille (max. 300 merkkiä)? Tuo ASCII-decimal lukujen yhteenlaskeminen onnistuisi nimittäin kätevästi fpu:n avulla BCD-luvuilla 18 decimaalinumeron mittaisiin lukuihin asti.

Mitä tulee tuohon add-funktioon ja silmukan ylimenoon. Ainahan saat noista yhteenlaskettavista luvuista samanmittaisia merkkijonoja lisäämällä lyhyemmän merkkijonon eteen oikean määrän etunollia.

Joo ikävä kyllä on tarvetta :)

Hmmm...tuossahan vois olla ideaa. Pitääpä yrittää tuossa illalla testailla.

Tarvikko esim tuossa main-funktiossa varata tuota tilaa sille taulukolle. Jos lukisin vaan fcanf tuon tiedoston rivit.

Esim jos tekisin että

#define KOKO 300

.....muut kooodit...

   char nimi[5];
   char numero[KOKO];
   FILE * pFile;

   pFile = fopen ("tiedosto.txt","r");
   rewind (pFile);
   fscanf (pFile, "%s", nimi);
   fscanf (pFile, "%s", numero);

Eikö tuo tekis tosta numero taulukosta vain sen mittaisen mitä se itseasiassa on?

tunkio [22.03.2011 21:16:52]

#

jalski kirjoitti:

Mitä tulee tuohon add-funktioon ja silmukan ylimenoon. Ainahan saat noista yhteenlaskettavista luvuista samanmittaisia merkkijonoja lisäämällä lyhyemmän merkkijonon eteen oikean määrän etunollia.

Heh, ei ristus. Voiko olla jollekki näin vaikeaa tämä? :) Pää hajoaa tämän koodin kanssa kohta. Ei tunnu edes onnistuvan tuo nollien lisääminen että sais molemmista numeroista yhtäpitkät. Silloinhan sen luulis toimivan ihan ok tuon add funktion.

No löytyipäs tuolta netin syövereistä esimerkki missä on käytetty noita "isoja numeroita" http://mailman.linuxchix.org/pipermail/courses/2002-November/001043.html

Näyttäs tuo pelittävän ihan sama onko toinen numero 3 ja toinen sen 100. Mutta miten saisin tähän lisättyä tuon tiedoston lukemisen. Miten saan luettua suoraan tiedostosta tuon toisen rivin N muuttujaan?

jalski [22.03.2011 23:27:50]

#

Olen viimeaikoina ohjelmoinut OS/2:lle PL/I - ohjelmointikielellä. Käyttämäni kääntäjä on vielä beta-asteella ja kaikkia yleisesti PL/I-kääntäjillä tuettuja juttuja ei vielä ole toteutettu.

Toteutin kuitenkin kokeeksi noilla helskutin isoilla kokonaisluvuilla laskemisen.

Alla oleva esimerkki lukee syötetiedostosta ensimmäisen rivin, millä pitäisi olla nimi ja kirjoittaa sen suoraan output.txt tiedostoon ensimmäiselle riville. Tämän jälkeen se lukee syötetiedostosta seuraavan rivin, millä pitäisi olla tuo maksimissaan 300 merkkiä pitkä kokonaisluku merkkijonona. Ohjelma tarkistaa, että kyseinen luku on oikean muotoinen ja lisää tarvittaessa etunollia. Käyttäjän syötteen lukemisen sijaan laitoin vain laiskuuttani tuon tiedostosta luetun luvun kanssa yhteenlaskettavan luvun suoraan ohjelmaan merkkijonona. Ohjelma lisää myös tuohon kyseiseen merkkijonoon tarvittavat etunollat. Sitten tehdään laskentahommelit ja tallennetaan tulos output.txt tiedostoon toiselle riville.

testi: proc(parm) options(main);
  dcl parm char(*) varying;
  dcl infile file record input env(TEXT);
  dcl outfile file record output env(TEXT);
  dcl buffer char(300) varying;
  dcl (luku1, luku2, carry) fixed dec(1) initial(0);
  dcl sum picture '99'; /* tulos ja carry */
  dcl i fixed bin(31);
  dcl c char(1);
  dcl inp char(300) varying initial('0');
  dcl zeros char(300) initial((300) '0');
  dcl result char(300) varying initial('');

  dcl (index, substr, length, verify) builtin;

  on endfile(infile) goto eof;
  on record(infile) goto conv_err;

  inp = '9999'; /* simuloitu käyttäjän syöte */

  open file(infile) title(parm);
  open file(outfile) title('output.txt');

  read file(infile) into(buffer);
  write file(outfile) from(buffer);

  read file(infile) into(buffer);
  if verify(buffer, '1234567890') > 0 then signal record(infile);

  if length(buffer) < 300 then
    buffer = substr(zeros, 1, (300 - length(buffer))) || buffer;

  if length(inp) < 300 then
    inp = substr(zeros, 1, (300 - length(inp))) || inp;

  do i=length(buffer) to 1 by -1;
    c = substr(buffer, i, 1);
    luku1 = c;
    c = substr(inp, i, 1);
    luku2 = c;
    sum = luku1 + luku2 + carry;
    result = result || substr(sum, 2,1);
    carry = substr(sum, 1,1);
  end;

  result = result || substr(sum, 1,1);

  buffer = '';

  do i = length(result) to 1 by -1;
    c = substr(result, i, 1);
    buffer = buffer || c;
  end;

  buffer = substr(buffer,verify(buffer, '0'), 300 - verify(buffer, '0') + 1);

  write file(outfile) from(buffer);

  goto eof;

 conv_err:
  put skip list ('All numbers in the input file must be integers separated with blanks and all lines must be terminated');

 eof:
  close file(infile);
  close file(outfile);

end testi;

tunkio [23.03.2011 08:20:50]

#

Jep, ei vaan ite tajua hirveesti tuosta PL/I -kielestä. Alkaa menemään vaan enemmän ja enemmän sekasi tässä hommassa, heh :) Mutta jotenkuten ymmärrettävältähän tuo näyttää.

Täytyisi nyt saada vaan omaan kalloon taottua että onko sitä ees oikeilla jäljillä. Tuohan se vois olla ratkaisu et tekis niistä mollemista yhtäpitkät lisäämällä etunollia pienemmän eteen, mutta kun ei sekään onnistu :)

jalski [23.03.2011 11:24:36]

#

Oma C-ohjelmointitaitoni on hiukan ruosteessa, mutta eikös tuo etunollien avulla molempien kasvattaminen yhtäpitkiksi onnistu jotenkin näin:

1. Määritä maksimipituinen merkkijono, mikä on täynnä nollia.

2. vertaa kumpi merkkijonoista on suurempi, käyttäjän syöte vai syötetiedostosta luettu.

3. vähennät suuremman merkkijonon pituudesta lyhyemmän merkkijonon pituuden, jolloin saat tarvittavan määrän etunollia tietoosi. Sitten vain käytät apunasi C++ string luokan tarjoamaa toiminnallisuutta ja liität nollia täynnä olevasta merkkijonosta oikean määrän nollia pienemmän merkkijonon eteen. Tai sitten käytät apupuskuria tuohon ja kopiot c-tyyliin ensin nuo etunollat strncpy() funktiolla ja sen jälkeen heti niiden perään alkuperäisen merkkijonon.


Oma kiireessä tehty toteutukseni muuten turhaan tällä hetkellä kasvattaa molemmista merkkijonoista aina maksimimittaisia etunollilla. Korjailen sen piruuttani illalla ja voisi olla hyvä lisätä myös overflow tieto siitä, jos carry on asettunut, kun maksimimäärä merkkejä on laskettu yhteen.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta