1

I want to be able to input a string (with spaces) and have it displayed like a moving sign. For example:

Input:

Hello World!  
5   (This signifies the number of characters the sign can hold)  

Output:

Sign #1:  
[Hello]  
[ello ]  
[llo W]  
[lo Wo]  
[o Wor]  
[ Worl]  
[World]  
[orld!]  
[rld! ]  
[ld! H]  
[d! He]  
[! Hel]  

This is what I have so far. If someone could direct me on what to do next I would greatly appreciate it!

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

int main() {
    int num_of_chars, i;
    char sign[30];
    char *arrayofsign = malloc(10 * sizeof(char));
    scanf("%[^\n]s", sign);
    arrayofsign = sign;
    printf("%s\n", arrayofsign);

    scanf("%d", &num_of_chars);
    for (i = 0; i < num_of_chars; i++) {
        printf("[]");
    }
}
chqrlie
  • 98,886
  • 10
  • 89
  • 149
UcfKnight
  • 45
  • 5
  • 1
    To print only the first 5 characters of a string, you can use `%5s` as format specifier. To specify where to start printing, you can use `&sign[index]` or `sign + index`. – a3f Feb 21 '16 at 07:17
  • 2
    Not directly related to your question but you have a memory leak: `char *arrayofsign = malloc(10*sizeof(char)); arrayofsign=sign;`. That allocates some memory and then immediately overwrites the pointer which means it can never be freed. – kaylum Feb 21 '16 at 07:20
  • 1
    To create the scrolling marquee, if you want the words to scroll on the same line, you will need to look at *ANSI escape sequences* to control cursor position. Look at `\033[K` (erase to end of line), `\033[s` (save cursor position), and `\033[u` (restore cursor position). – David C. Rankin Feb 21 '16 at 07:20
  • 1
    Surely, OP only needs carriage-return? – Martin James Feb 21 '16 at 07:22
  • 1
    @a3f: not exactly: to print the first 5 characters of a string, the format specifier is `%.5s`. The number of characters can be passed as an `int` before the string pointer with `%.*s`. – chqrlie Feb 21 '16 at 08:05

8 Answers8

3

Use carriage return '\r' to move the cursor back to the beginning of a line. Assuming a POSIX system (because of the use of nanosleep()), you could code like this. Note the use of the %*.*s notation to specify how long a sub-section of the string to print.

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

int main(void)
{
    char sign[50];
    struct timespec d = { .tv_sec = 0, .tv_nsec = 100000000 };

    printf("What should I say? ");

    if (scanf("%49[^\n]", sign) == 1)
    {
        printf("Sign: [%s]\n\n", sign);

        int t_len = strlen(sign);
        for (int i = 0; i < 10; i++)
        {
            for (int l_len = 0; l_len < t_len; l_len++)
            {
                int r_len = t_len - l_len;
                /* Rotate to right */
                //printf("\r[%*.*s%*.*s]", l_len, l_len, sign + r_len, r_len, r_len, sign);
                /* Rotate to left */
                printf("\r[%*.*s%*.*s]", r_len, r_len, sign + l_len, l_len, l_len, sign);
                fflush(stdout);
                nanosleep(&d, 0);
            }
        }
        putchar('\n');
    }
    return 0;
}

The output at the end was:

What should I say? Hello World, and how are you today?
Sign: [Hello World, and how are you today?]

[?Hello World, and how are you today]

It would be better if the code added a string such as " ... " after the entered text so it wraps better. That is trivial to do if you reserve enough space in the string for the padding on input (change 49 into 44 since there are five characters in the padding).

Piping the output through tr '\r' '\n' yields:

Hello World, and how are you today?
What should I say? Sign: [Hello World, and how are you today?]


[Hello World, and how are you today?]
[ello World, and how are you today?H]
[llo World, and how are you today?He]
[lo World, and how are you today?Hel]
[o World, and how are you today?Hell]
[ World, and how are you today?Hello]
[World, and how are you today?Hello ]
[orld, and how are you today?Hello W]
[rld, and how are you today?Hello Wo]
[ld, and how are you today?Hello Wor]
[d, and how are you today?Hello Worl]
[, and how are you today?Hello World]
[ and how are you today?Hello World,]
[and how are you today?Hello World, ]
[nd how are you today?Hello World, a]
[d how are you today?Hello World, an]
[ how are you today?Hello World, and]
[how are you today?Hello World, and ]
…

which shows how the output changes over time. It also illustrates the problems with piping standard output to another command.

An alternative (simpler) version of the printf() statements:

                /* Rotate to right */
                putchar('\r');
                printf("[%.*s%.*s]", l_len, sign + r_len, r_len, sign);
                printf("    ");
                /* Rotate to left */
                printf("[%.*s%.*s]", r_len, sign + l_len, l_len, sign);

That code shows the text scrolling both to the right and to the left at once. In this context, the leading * in the %*.*s conversion specification isn't needed (but there are others where it can be useful and even necessary), so only one length argument is needed for each string.

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
1

It's not exactly clear from the question what problem are you having, but I hope that a working source code example will help you anyway.

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


#ifdef _WIN32
    #include <windows.h>
#else
    #include <unistd.h>
#endif

//There is no built-in "sleep" in C99, hence this function. You can use any method you want to implement a delay.
void customSleep( int seconds )
{   // Pretty cross-platform, both ALL POSIX compliant systems AND Windows
    #ifdef _WIN32
        Sleep( 1000 * seconds );
    #else
        sleep( seconds );
    #endif
}

int main(){
    char text[30];
    int signLength;

    printf("Enter text: ");
    scanf ("%[^\n]%*c", text); //Reading a line with spaces.
    printf("Enter sign length: ");
    scanf("%d", &signLength);

    printf("SignLength: %d\n", signLength);
    printf("Text: %s\n", text);

    int currentStartPosition = 0;

    setbuf(stdout, NULL); //disable buffering for stdout. Otherwise, if the string is short, it doesn't print immediately.
    //Alternatively, you could print a new line character at the end.
    while (1) {
        for (int i = 0; i < signLength; ++i) {
            int indexOfCharacterToPrint = (currentStartPosition + i) % strlen(text);
            printf("%c", text[indexOfCharacterToPrint]);
        }

        ++currentStartPosition;
        customSleep(1);

        //Stole this method from other answers :)
        printf("\r");
    }
}

Additional links:

  1. Implement time delay in C (SO question).
  2. Why does printf not flush after the call unless a newline is in the format string? (SO question)
Community
  • 1
  • 1
FreeNickname
  • 7,203
  • 2
  • 25
  • 56
  • It works, but using `'\r'` once instead of `'\b'` many times is arguably more efficient, if efficiency matters in a program which spends most of every second asleep. – Jonathan Leffler Feb 21 '16 at 07:57
  • @JonathanLeffler, Thanks for testing out my code. I agree with you on efficiency, but I replaced `\b`s with `\r` mostly because it is more elegant, rather than efficient. – FreeNickname Feb 21 '16 at 08:16
0

You should repeatedly output the signage display, go back to the beginning of the line with \r, wait a bit and start again with starting point one position to the right in the signage string:

#include <stdio.h>
#include <unistd.h>

int main(void) {
    int num_of_chars;
    size_t i;
    char sign[30];
    char arrayofsign[60];

    if (scanf("%29[^\n]", sign) != 1 || *sign == '\0') {
        printf("invalid input\n");
        exit(1);
    }
    strcpy(arrayofsign, sign);
    while (strlen(arrayofsign) < sizeof(arrayofsign) - 1) {
        strcat(arrayofsign, " ");
        strncat(arrayofsign, sign,
                sizeof(arrayofsign) - 1 - strlen(arrayofsign));
    }
    if (scanf("%d", &num_of_chars) != 1 || num_of_chars <= 0) {
        printf("invalid input\n");
        exit(1);
    }
    for (i = 0;; i = (i + 1) % strlen(sign)) {
        printf("[%.*s]\r", num_of_chars, arrayofsign + i);
        fflush(stdout);
        sleep(1);
    }
    return 0;
}
chqrlie
  • 98,886
  • 10
  • 89
  • 149
0

Do you mean the following?

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

int main( void )
{
    char s[] = "Hello World!";
    size_t n = strlen( s );
    size_t m = 5;

    for ( size_t i = 0; i < n; i++ )
    {
        putchar( '[' );
        for ( size_t j = 0; j < m; j++ ) 
        {
            char c = s[(i + j) % ( n + 1 )];
            if ( !c ) c = ' ';                       
            putchar( c );
        }            
        printf( "]\n" );
    }        
}    

The program output is

[Hello]
[ello ]
[llo W]
[lo Wo]
[o Wor]
[ Worl]
[World]
[orld!]
[rld! ]
[ld! H]
[d! He]
[! Hel]

If so then all you need to do is to add inputting of the string and the value of the variable m.

Vlad from Moscow
  • 224,104
  • 15
  • 141
  • 268
0

I suggest creating a duplicate string that contains the original string, twice. In that case the printing becomes really simple.

Obtain the length of the string, allocate a new string with double the length (don't forget to allocate space for the null terminator), then copy the string into the new string twice. The new string will look like this:

char string[] = "Hello world!" ;
...
char new_string[] = "Hello world!Hello world!" ;

Then simply print it from an offset until you reach the length of the original string and reset the index:

size_t i = 0;
while( 1 )
{
    printf( "%.5s" , new_string+i );
    i++;
    if( i == string_length )
    {
        i = 0;
    }
    MoveCursor();
    Delay();
}

The number 5 in the printf format syntax "%.5s", means that at most 5 characters of new_string will be printed.

Function MoveCurson should reset the printing position in the console to the start of the line. This function has to be implemented by you, you can start here: Update printf value on same line instead of new one

Function Delay must be implemented by you and it should pause the program for a while. Doing this is platform specific so there is not single answer.

2501
  • 24,549
  • 4
  • 42
  • 83
0

The following program prints all substrings only once:

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

int main(void)
{
    int num_of_chars, i;
    char sign[30], two_sign[60];
    fgets(sign, sizeof sign, stdin);
    scanf("%d", &num_of_chars);

    sign[strlen(sign) - 1] = '\0'; // discard '\n'
    strcpy(two_sign, sign);
    strcat(two_sign, sign);
    for (i = 0; sign[num_of_chars + i]; i++)
    {
        printf("[%.*s]\n", num_of_chars, two_sign + i);
    }

    return 0;
}

The %.*s enables me to specify the max length of the string to be printed by num_of_chars.

nalzok
  • 11,870
  • 17
  • 57
  • 105
0

This is what I whipped up real quick.

Commented every line of code.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h> /* Use this if windows */
/* #include <unistd.h> for unix and change Sleep to sleep */

char * marquee(char * string, int length, int index)
{
    /* string = "Hello, World!!" */

    /* Display holds five characters and one for the null */
    char * display = malloc(sizeof(char) * (length + 1));

    /* This pointer will walk down the string for us */
    /* For example: "Hello" then "ello " then "llo H", etc. */
    char * travel = malloc(sizeof(char) * 256);

    /* This pointer is to concatenate the string that moves off the screen back to the end of travel.  This is necessary for the marquee effect. */
    /* For example: "Hello World!! " then "ello World!! H" then "llo World!! He" */
    char * temp = malloc(sizeof(char) * (strlen(string) + 1));

    /* We need a counter to walk through the string */
    int counter = 0;

    /* Travel should start at the beginning of the string */
    /* For example: travel = "Hello, World!!" */
    strcpy(travel, string);

    /* Loop through string */
    while (counter < index)
    {
        /* First letter of travel needs to equal temp */
        /* For example: */
        /* First pass:  travel[0] = 'H' temp[0] = 'H' */
        /* Second pass: travel[0] = 'e' temp[1] = 'e' /*
        /* Third pass:  travel[0] = 'l' temp[2] = 'l' /*
        /* etc... */
        temp[counter] = travel[0];

        /* Walk down the string */
        /* For example: travel = "Hello, World!!", then "ello, World!!", then "llo, World!!" etc. */
        travel++;

        /* Increment counter to loop through string */
        counter = counter + 1;
    }

    /* We need a null at the end */
    temp[counter + 1] = '\0';

    /* If we don't add a space at the end, we get a "ello, World!!H" on the next line */
    strcat(travel, " ");

    /* Second pass: "ello, World!! H"  Third pass: "llo, World!! He", etc. */
    strcat(travel, temp);

    /* Display = "Hello", then "ello ", then "llo H", etc. */
    strncpy(display, travel, length);

    /* Add the null or else */
    display[length] = '\0';

    return display;
}

/* This function clears the screen to give the marquee effect.
   The following is a system call to the operating system to do the clearing. */
void clrscr()
{
    system("@cls||clear");
}

int main()
{
    /* Our starting string */
    char * string = "Hello, World!!";

    /* Get length of string, we'll use it later */
    int len = strlen(string);

    /* My version of C sucks, so I have to declare this outside the loop*/
    int i;

    /* Infinite Loop, so marquee never dies */
    while (1)
    {
        /* Loop through entire length of string */
        for (i = 0; i < len; i++)
        {
            /* Get five character string from marquee function */
            char * temp = marquee(string, 5, i);

            /* Print string...which is only 5 characters big */
            printf("%s\n", temp);

            /* Sleep for one second or else it will move by too fast to see */
            Sleep(1000);

            /* Clear Screen */
            clrscr();
        }
    }

    return 0;
}

Works, but just realized I didn't bother freeing memory. Hah. Too used to javascript.

You might have to comment out windows.h and comment in unistd.h if you're using UNIX. Also, you would need to make Sleep, sleep (notice the lowercase 's') if you're on UNIX.

MrPickles
  • 1,085
  • 1
  • 15
  • 31
-2
// I think i should not help you to do your homework.

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

void marquee(const char* input, unsigned int loopNumber) {
    int start = 0;
    size_t inputLen = strlen(input);
    while (loopNumber--) {
        printf("[");
        if (start == inputLen) start = 0;
        int end = start + 5;
        if (end > inputLen) end -= inputLen;
        for (int i = start; i != end; i++) {
            if (i == inputLen) i = 0;
            printf("%c", input[i]);
        }
        printf("]\n");
        start++;
    }
}

int main() {
    marquee("Hello world! ", 10);
    return 0;
}