问题
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("[]");
}
}
回答1:
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.
回答2:
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:
- Implement time delay in C (SO question).
- Why does printf not flush after the call unless a newline is in the format string? (SO question)
回答3:
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
.
回答4:
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;
}
回答5:
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.
回答6:
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
.
回答7:
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.
回答8:
// 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;
}
来源:https://stackoverflow.com/questions/35533350/how-to-output-a-string-in-a-marquee-fashion