Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

Καλησπέρα.
Θα ήθελα μια μικρή βοήθεια μ'ένα πρόβλημα που συναντάω.
 
Έστω λοιπόν πως ο χρήστης εισάγει ένα string.
//Αποθηκεύεται σε string array αδιάφορου μήκους
Σκοπός μας είναι να τυπώσουμε το string επεξεργασμένο:
Σε κάθε κενό που συναντάμε, να αφαιρούμε τους τελευταίους 2 χαρακτήρες της προηγούμενης λέξης.
//Δεν έχει γίνει διευκρίνηση αν η λέξη είναι μήκους 1 ή 2, να κάνουμε κάτι συγκεκριμένο ή όχι.
Σκέφτηκα λοιπόν να το υλοποιήσω με 2 πίνακες (ο ένας θα'χει τό αρχικό string και ο άλλος το τελικό)
και με μια συνάρτηση:

  • void trim_word()

Αλλά αυτό που με κολλάει είναι η αφαίρεση των δύο τελευταίων χαρακτήρων από τη λέξη.
Σκέφτηκα και υλοποίησα την αντικατάσταση των δύο τελευταίων χαρακτήρων από τη λέξη με ειδικό χαρακτήρα (beep) και κατά την εμφάνιση του 'τελικού' string να τον απορρίπτω, αλλά δεν μπορώ να σκεφτώ κάποιον ουσιαστικό τρόπο να τους αφαιρέσω εντελώς.
 
Θα ήθελα τα φώτα όσων έχουν περισσότερη εμπειρία και γνώση στο αντικείμενο.

 

Ευχαριστώ εκ των προτέρων όποιον ασχοληθεί.
 
Εδώ είναι ο κώδικας που έχω μέχρι στιγμής.

Δημοσ.

Όπως το έχεις τώρα, θέτεις το κάθε χαρακτήρα του αρχικού string στο τελικό και αν αυτό είναι space, τότε "αφαιρείς" τους χαρακτήρες θέτοντας την τιμή 7. Έτσι όμως, όπως λες, έχεις το πρόβλημα του πώς να τους αφαιρέσεις. Ένας τρόπος θα ήταν να έχεις δύο μεταβλητές counter μία για κάθε string έτσι ώστε όταν βρίσκεις space, να μειώνεις τον counter του τελικού string. Έτσι ο επόμενος χαρακτήρας θα "γραφτεί" πάνω σε αυτόν που θέλεις να σβήσεις.

 

Άλλος τρόπος θα είναι να αλλάξεις τον αλγόριθμο σου ώστε σε κάθε ανακύκλωση να ελέγχει τον counter+2 χαρακτήρα και αν αυτός είναι space να πηδάει δύο χαρακτήρες χωρίς να τους θέτει καν στο τελικό string. Για ακόμη μεγαλύτερη ευκολία μπορείς και εδώ να χρησιμοποιήσεις δύο μεταβλητές counter μία για το κάθε string.

  • Like 1
Δημοσ. (επεξεργασμένο)

Νομιζω μπορεις και μ εναν πινακα, αφου σβηνεις προηγουμενους χαρακτηρες κ οχι επομενους ;) σκεψου το

 

 

int i = 0, j = 0;
int len = strlen(str);
while (i < len)
{
  if (str[i] == ' ')
  {
    j -= 2;
  }
  str[j] = str[i];
  ++i;
  ++j;
}

while (j < len)
{
  str[j] = '\0';
  ++j;
}

 

 

Επεξ/σία από nilosgr
  • Like 2
Δημοσ.

Αυτό που έγραψε ο nilos, μόνο που δεν χρειάζεται ούτε η strlen() ούτε η τελευταία λούπα. Χρειάζεται όμως να κάνει check ώστε το j να μην γίνεται αρνητικό όταν η φράση έχει λιγότερα από 2 γράμματα:

 

 

#include <stdio.h>

int main( void )
{
	char s[BUFSIZ] = {'\0'};
	int i, j;

	puts( "Insert string:" );
	fgets( s, BUFSIZ, stdin );

	for (i=0,j=0; '\0' != s[i]; i++) {
		if ( i > 2 && ' ' == s[i] ) {
			j -= 2;
		}
		s[j++] = s[i];
	}
	s[j] = '\0';

	puts( s );
	return 0;
}

 

Ίσως μου εχει ξεφύγει και κάνας άλλος έλεγχος, τσέκαρέ το με διάφορα test-cases.

Δημοσ.

Νομιζω μπορεις και μ εναν πινακα, αφου σβηνεις προηγουμενους χαρακτηρες κ οχι επομενους ;) σκεψου το

int i = 0, j = 0;

int len = strlen(str);

while (i < len)

{

if (str == ' ')

{

j -= 2;

}

str = str[j];

++i;

++j;

}

 

 

Κατάλαβα τον τρόπο σκέψης σου, με τον πρώτο βρόγχο, αλλά δεν λειτουργεί.

Στο πρώτο κενό βάζει τους δύο τελευταίους χαρακτήρες της πρώτης λέξης τη μια δίπλα στην άλλη μέχρι να ολοκληρωθεί το μήκος του str.

Έστω πως εισάγεις "kalispera sas", εμφανίζει "kalisperarara"  :whistle:

Νομιζω μπορεις και μ εναν πινακα, αφου σβηνεις προηγουμενους χαρακτηρες κ οχι επομενους ;) σκεψου το

while (j < len)
{
  str[j] = '\0';
  ++j;
}

To '\0' δεν ορίζει το τέλος ενός string; Για ποιον λόγο να γεμίσουμε τον υπόλοιπο πίνακα με '\0' ;

 

 

Αυτό που έγραψε ο nilos, μόνο που δεν χρειάζεται ούτε η strlen() ούτε η τελευταία λούπα. Χρειάζεται όμως να κάνει check ώστε το j να μην γίνεται αρνητικό όταν η φράση έχει λιγότερα από 2 γράμματα:

    char s[bUFSIZ] = {'\0'};

    fgets( s, BUFSIZ, stdin );

    puts( s );

 

Ίσως μου εχει ξεφύγει και κάνας άλλος έλεγχος, τσέκαρέ το με διάφορα test-cases.

 

Έχω ήδη τροποιήσει τον έλεγχο ώστε να μην γίνεται αρνητικό το j

Αν μου εξηγήσεις αυτές οι 3 γραμμές τι κάνουν, θα κατανοήσω στο 100% τον κώδικα σου.

 

 

Επίσης μου έγιναν διευκρινίσεις:

Αν υπάρχουν πάνω από ένα space σε σειρά, να μειώνονται σε 1.

Αν το μήκος της λέξης πριν το space έχει μήκος <=2, να αγνοείται από τον κώδικα.

 

οπότε θα πρέπει να αλλάξω όλη τη δομή του κώδικα μου και να το πάω αλλιώς.

Η μόνη λύση που μου'ρχεται πρόχειρα τώρα που ειδα τις διευκρινίσεις,είναι την κάθε λέξη να την πετάω σ'έναν πίνακα και απο'κει να την επεξεργαστώ αν πληροί τις προϋποθέσεις.

Τέλος αντί να κάτσω να αφαιρέσω τυχόν περισσότερα εισαχθέα space, απλά να τυπώσω τον έναν πίνακα μετά τον άλλον.

 

 

 

 

Ευχαριστώ όσους ασχολήθηκαν, και θα σχοληθούν. :-)

Δημοσ.

...

 

Έχω ήδη τροποιήσει τον έλεγχο ώστε να μην γίνεται αρνητικό το j

Αν μου εξηγήσεις αυτές οι 3 γραμμές τι κάνουν, θα κατανοήσω στο 100% τον κώδικα σου.

 

 

1. char s[BUFSIZ] = {'\0'};    // ορισμός & αρχικοποίηση ενός string s μέγιστου μήκους BUFSIZ chars
2. fgets( s, BUFSIZ, stdin );  // διάβασμα του s από το πληκτρολόγιο (stdin)
3. puts( s );                  // τύπωμα του string s στην οθόνη και αλλαγή γραμμής

 

 

Έβαλα επεξηγηματικά σχόλια, αλλά μερικές ακόμα διευκρινήσεις:

 

1. η σταθερά BUFSIZ ορίοζεται στο stdio.h (και αλλάζει από πλατφρομα σε πλατφόρμα)

 

2. η gets() είναι καταργημένη στη C11, και ήδη σημειωμένη "προς κατάργηση" στην C99. Η χρήση της είναι επικίνδυνη. Η fgets() είναι πιο ασφαλής, γιατί απαιτεί να της δώσεις και το μέγιστο μήκος του string σου. Επίσης διατηρεί το '\n' στο τέλος του string (εκτός αν δοθεί φράση με περισσότερα του BUFSIZ-1 chars), αλλά στην συγκεκριμένη άσκηση μάλλον δεν σε ενδιαφέρει, οπότε δεν έκανα τον σχετικό έλεγχο.

 

3. όταν απλώς θέλεις να τυπώσεις ένα string και να αλλάξεις γραμμή στην οθόνη, δεν χρειάζεται η printf().

 

Επίσης μου έγιναν διευκρινίσεις:

οπότε θα πρέπει να αλλάξω όλη τη δομή του κώδικα μου και να το πάω αλλιώς.

Η μόνη λύση που μου'ρχεται πρόχειρα τώρα που ειδα τις διευκρινίσεις,είναι την κάθε λέξη να την πετάω σ'έναν πίνακα και απο'κει να την επεξεργαστώ αν πληροί τις προϋποθέσεις.

Τέλος αντί να κάτσω να αφαιρέσω τυχόν περισσότερα εισαχθέα space, απλά να τυπώσω τον έναν πίνακα μετά τον άλλον.

Δεν χρειάζονται 2 πίνακες, είναι άσκοπη σπατάλη χρόνου και χώρου, μιας και ο in-place κώδικας είναι εύκολος (αν ήταν τίποτα πολύπλοκο, θα είχε περισσότερο νόημα να μην το υλοποιούσες in-place). 

 

Στον κώδικα που σου έδωσα στο προηγούμενο ποστ, απλώς προσθέτεις άλλο ένα conditional για να αγνοεί τυχόν συνεχόμενα spaces:

 

 

 

#include <stdio.h>

int main( void )
{
	char s[BUFSIZ] = {'\0'};
	int i, j;

	puts( "Insert string:" );
	fgets( s, BUFSIZ, stdin );

	for (i=0,j=0; '\0' != s[i]; i++) {
		if ( ' ' == s[i] && ' ' == s[i+1] ) {
			continue;
		}
		if ( i > 2 && ' ' == s[i]) {
			j -= 2;
		}
		s[j++] = s[i];
	}
	s[j] = '\0';

	puts( s );
	return 0;
}

 

Πάλι, μπορεί να περιέχει τυχόν αβλεψίες,  οπότε για σιγουριά τσέκαρέ το με πολλά tets-cases και αν χρειαστεί διόρθωσε το.

 

Ευχαριστώ όσους ασχολήθηκαν, και θα σχοληθούν. :-)

Παρακαλώ :) Οι βασικοί λόγοι όμως που σου δίνω κι εγώ έτοιμο κώδικα είναι αφενός διότι είχε ήδη δοθεί από τον nilos, κι αφετέρου επειδή είναι εύκολη & ανώδυνη άσκηση, οπότε το να μελετήσεις υλοποιήσεις της μπορεί να σε "ξεκλειδώσει" για τις επόμενες ασκήσεις.

Δημοσ.

Επίσης καλό είναι να μην χρησιμοποιείς global μεταβλητές τουλάχιστον όχι σε αυτό το επίπεδο. ό,τι θες πέρνα το με pointer ( και βασικά το string είναι από μόνο του pointer ). Δε ξέρω αν το έκανες εδώ έτσι απλά χάριν ευκολίας απλά το λέω μιας και δε βλέπω να έχει αναφερθεί. Δεν είναι καλή συνήθεια.

 

Βάζω κι εγώ μια υλοποίηση με σπάσιμο σε tokens με strtok για ποικιλία. :P

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

#define BUFSIZE 256

int main( void )
{
    char str[ BUFSIZE ], new_str[ BUFSIZE ], *token;
    int i = 0;

    printf( "String: " );
    fgets( str, BUFSIZE, stdin );
    
    token = strtok( str, " \t\n" );
    while ( token != NULL ) {
        if ( token[1] != '\0' && token[2] != '\0' ) {
            while ( token[2] != '\0' ) {
                new_str[ i++ ] = *token++;
            }
            new_str[ i++ ] = ' ';
        }
        token = strtok( NULL, " \t\n" );
    }
    new_str[i] = '\0';
        
    puts( new_str );
    return 0;
}
  • Like 1
Δημοσ.

Όχι πως έχει ιδιαίτερη σημασία για την συγκεκριμένη άσκηση, αλλά σαν γενικότερο C mindset δες διαφορά μεταξύ in-place και out-of-place με strtok:

 

 

 

10Mb:

Preparing a string of 10485761 bytes... ready

[in-place]
mem: 10485761 bytes
0.016 secs

[out-of-place using strtok]
mem: 20971522 bytes
0.063 secs
100Mb:

Preparing a string of 104857601 bytes... ready

[in-place]
mem: 104857601 bytes
0.141 secs

[out-of-place using strtok]
mem: 209715202 bytes
0.718 secs
500Mb:

Preparing a string of 524288001 bytes... ready

[in-place]
mem: 524288001 bytes
0.672 secs

[out-of-place using strtok]
mem: 1048576002 bytes
3.359 secs
1000MB:

Preparing a string of 1048576001 bytes... ready

[in-place]
mem: 1048576001 bytes
1.328 secs

[out-of-place using strtok]
mem: 2097152002 bytes
6.688 secs

 

Το string το γεμίζω με 'a' και του βάζω ' ' κάθε 7 γράμματα (θεώρησα πως περίπου τόσο είναι το μέσο μήκος λέξης).

 

 

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

#define MB(n)   ( (n) * 1024 * 1024 )
#define MAXSIZ  ( MB(50) + 1 )

/* --------------------------------------------------
 *
 * --------------------------------------------------
 */
int main( void )
{
	clock_t tend, tstart;
	double  cpu_time_used = 0.0;

//	char s[BUFSIZ] = "Hello   there   ,   how are you?";
	char *s = malloc( MAXSIZ );
	if ( NULL == s ) {
		return 1;
	}

	long i, j;

	printf( "Preparing a string of %ld bytes... ", MAXSIZ );
	fflush( stdout );
	for (long i=0; i < MAXSIZ-1; i++) {
		s[i] = (0 == i % 7) ? ' ' : 'a';
	}
	s[MAXSIZ-1] = '\0';
	puts( "ready\n" );

//	puts( "Insert string:" );
//	fgets( s, BUFSIZ, stdin );

	/*
	 * in-place: migf1
	 */

	puts( "[in-place]" );
	printf( "mem: %ld bytes\n", MAXSIZ );

	tstart = clock();

	for (i=0,j=0; '\0' != s[i]; i++) {
		if ( ' ' == s[i] && ' ' == s[i+1] ) {
			continue;
		}
		if ( i > 2 && ' ' == s[i]) {
			j -= 2;
		}
		s[j++] = s[i];
	}
	s[j] = '\0';

	tend = clock();

	cpu_time_used = ((double) (tend - tstart)) / CLOCKS_PER_SEC;
	printf( "%g secs\n\n", cpu_time_used );

	/*
	 * out-of-place using strtok: the other one
	 */

	puts( "[out-of-place using strtok]" );
	printf( "mem: %ld bytes\n", 2 * MAXSIZ );

	tstart = clock();

	char new_str[MAXSIZ], *token;
	i = 0;
	token = strtok( s, " \t\n" );
	while ( token != NULL ) {
		if ( token[1] != '\0' && token[2] != '\0' ) {
			while ( token[2] != '\0' ) {
				new_str[ i++ ] = *token++;
			}
			new_str[ i++ ] = ' ';
		}
		token = strtok( NULL, " \t\n" );
	}
	new_str[i] = '\0';

	tend = clock();
	cpu_time_used = ((double) (tend - tstart)) / CLOCKS_PER_SEC;
	printf( "%g secs\n\n", cpu_time_used );

//	puts( s );

	free( s );
	return 0;
}

 

Η strtok() είναι βαριά συνάρτηση. Επίσης, όσο μεγαλύτερο είναι το input-data, τόσο λιγότερο μας εξυπηρετεί το out-of-place. Σε γενικές γραμμές, προτιμάμε out-of-place όταν μας δίνει σημαντικό πλεονέκτημα είτε σε ταχύτητα εκτέλεσης είτε σε απλοποίηση του αλγόριθμου. Στην προκειμένη νομίζω δεν ισχύει τίποτα από τα δυο ;)

Δημοσ.

Όχι πως έχει ιδιαίτερη σημασία για την συγκεκριμένη άσκηση, αλλά σαν γενικότερο C mindset δες διαφορά μεταξύ in-place και out-of-place με strtok:

 

 

 

10Mb:

Preparing a string of 10485761 bytes... ready

[in-place]
mem: 10485761 bytes
0.016 secs

[out-of-place using strtok]
mem: 20971522 bytes
0.063 secs
100Mb:

Preparing a string of 104857601 bytes... ready

[in-place]
mem: 104857601 bytes
0.141 secs

[out-of-place using strtok]
mem: 209715202 bytes
0.718 secs
500Mb:

Preparing a string of 524288001 bytes... ready

[in-place]
mem: 524288001 bytes
0.672 secs

[out-of-place using strtok]
mem: 1048576002 bytes
3.359 secs
1000MB:

Preparing a string of 1048576001 bytes... ready

[in-place]
mem: 1048576001 bytes
1.328 secs

[out-of-place using strtok]
mem: 2097152002 bytes
6.688 secs

 

Το string το γεμίζω με 'a' και του βάζω ' ' κάθε 7 γράμματα (θεώρησα πως περίπου τόσο είναι το μέσο μήκος λέξης).

 

 

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

#define MB(n)   ( (n) * 1024 * 1024 )
#define MAXSIZ  ( MB(50) + 1 )

/* --------------------------------------------------
 *
 * --------------------------------------------------
 */
int main( void )
{
	clock_t tend, tstart;
	double  cpu_time_used = 0.0;

//	char s[BUFSIZ] = "Hello   there   ,   how are you?";
	char *s = malloc( MAXSIZ );
	if ( NULL == s ) {
		return 1;
	}

	long i, j;

	printf( "Preparing a string of %ld bytes... ", MAXSIZ );
	fflush( stdout );
	for (long i=0; i < MAXSIZ-1; i++) {
		s[i] = (0 == i % 7) ? ' ' : 'a';
	}
	s[MAXSIZ-1] = '\0';
	puts( "ready\n" );

//	puts( "Insert string:" );
//	fgets( s, BUFSIZ, stdin );

	/*
	 * in-place: migf1
	 */

	puts( "[in-place]" );
	printf( "mem: %ld bytes\n", MAXSIZ );

	tstart = clock();

	for (i=0,j=0; '\0' != s[i]; i++) {
		if ( ' ' == s[i] && ' ' == s[i+1] ) {
			continue;
		}
		if ( i > 2 && ' ' == s[i]) {
			j -= 2;
		}
		s[j++] = s[i];
	}
	s[j] = '\0';

	tend = clock();

	cpu_time_used = ((double) (tend - tstart)) / CLOCKS_PER_SEC;
	printf( "%g secs\n\n", cpu_time_used );

	/*
	 * out-of-place using strtok: the other one
	 */

	puts( "[out-of-place using strtok]" );
	printf( "mem: %ld bytes\n", 2 * MAXSIZ );

	tstart = clock();

	char new_str[MAXSIZ], *token;
	i = 0;
	token = strtok( s, " \t\n" );
	while ( token != NULL ) {
		if ( token[1] != '\0' && token[2] != '\0' ) {
			while ( token[2] != '\0' ) {
				new_str[ i++ ] = *token++;
			}
			new_str[ i++ ] = ' ';
		}
		token = strtok( NULL, " \t\n" );
	}
	new_str[i] = '\0';

	tend = clock();
	cpu_time_used = ((double) (tend - tstart)) / CLOCKS_PER_SEC;
	printf( "%g secs\n\n", cpu_time_used );

//	puts( s );

	free( s );
	return 0;
}

 

Η strtok() είναι βαριά συνάρτηση. Επίσης, όσο μεγαλύτερο είναι το input-data, τόσο λιγότερο μας εξυπηρετεί το out-of-place. Σε γενικές γραμμές, προτιμάμε out-of-place όταν μας δίνει σημαντικό πλεονέκτημα είτε σε ταχύτητα εκτέλεσης είτε σε απλοποίηση του αλγόριθμου. Στην προκειμένη νομίζω δεν ισχύει τίποτα από τα δυο ;)

 

 

In και out of place υποθέτω θα εννοείς το αν θα επεξεργάζεται το ίδιο το string ή αν θα παράγει καινούργιο. Εντάξει γενικά κι εγώ αυτό δε το βαλα για κάτι πιο γρήγορο και διαισθητικά αν το δεις δηλαδή, η πρώτη κλήση της συνάρτηση μόνο, τρέχει όλο το string. Σίγουρα κάνει παραπάνω ελέγχους.

 

Τώρα οκ προσωπικά η λογική που ακολουθω εγώ είναι πως δεν ασχολούμαι τόσο με το performance με την προϋπόθεση πως το dataset που έχεις ή η πολυπλοκότητα του προγράμματος είναι αρκούντως μικρή ώστε να μη δεις διαφορά. Εκεί προτιμώ να δίνω περισσότερη σημασία στην αναγνωσιμότητα του προγράμματος. Και όσες λίγες φορές έχει τύχει να αντιμετωπίσω πρόβλημα με τέτοια θέματα είναι κάτι αρκετά χτυπητό ( πχ αναδρομή ). Πιθανότατα θα χει να κάνει και το γεγονός πως ακόμα ασχολούμαι μόνο με projects εντός της σχολής δε ξέρω.

 

Πάντως η strtok αργή ή γρήγορη βολεύει αρκετά εδώ οκ δεν έχει να κάνει τόσο αλλά αν θες να κάνεις πχ κάποιο αντίστοιχο parsing έρχεται κουτί.

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...