Inputting multiple lines of strings in C - c

I'm fairly new to coding and am currently taking a programming course at school with C. We were given an assignment and I'm having a bit of difficulty with the first part. We're learning how to use the string-handling library (stdlib.h) and the objective of the assignment is to input multiple lines of text from the keyboard. The instructor advised us to use two-dimensional arrays in order to do this, but I'm a bit stuck. Here's the code I've written:
int main(void) {
char string[3][SIZE];
int i, j;
int c;
printf("Enter three lines of text:\n");
for (i = 0; i < 3; i++) {
j = 0;
while ((j < SIZE) && (c = getchar() != '\n')) {
string[i][j] = c;
j++;
}
}
for (i = 0; i < 3; i++) {
for (j = 0; j < SIZE; j++) {
printf("%c", string[i][j]);
}
printf("\n");
}
return 0;
}
Some points that I'd like to make are that I used the getchar() function to receive input one character at a time, and also the second for loop I intended to print each line of text that is stored in each row of the string array.
The input is any string of text for three lines, for example:
Hi my name is John.\n
I am from the US\n
and I'm a student.
Here's what the current output looks like:
Enter three lines of text:
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr...
The output that I'm expecting is:
Enter three lines of text:\n
Hi my name is John.\n
I'm from the US\n
and am a student.
Any tips or advice would be greatly appreciated. Thank you!

First of all let me commend the fact the you starting your way with C. That's the most solid language to learn(better is only assembly itself) - you will have a full understanding of how things work, which you wouldn't get if started from some language written on top of C(like Java and Python).
But it's a hard and long road, which worth that.
On the code: there is a lot going and you have made a lot of amusing bugs that would reproduce different interesting things every other time and machine you run it.
First of all: to make your code work somehow all you need is add parenthesis:
while ((j < SIZE) && ((c = getchar()) != '\n')) {
In C everything is binary(or integer, depending how you look at it) and default binding is to the right a op1 b op2 c op3 d..
First op3 is evaluated c op3 d = r1, then you have a op1 b op2 r1 and so on.
Thus you was comparing the value of getchar() with value of character '\n' - which are not equal, so you get TRUE (value 1) and store it in local variable c.
Next you still have some problems because of the way you initialized your array:
char string[3][SIZE];
What it does is simply "intrusts" 3*SIZE*sizeof(char) bytes of you process address space to a thing labeled "string". But that does not clear up all the remnants of previous live (of your program, or even before) on those bytes, so if it happens that SIZE in your program == 100 and you used to store your credit card on a real address memory (RAM) mapped to that region of your program memory you would see your credit card when you print it by printf - if you didn't overwrite those 300 bytes.
This may help you looking at it:
#include <stdlib.h>
#include <stdio.h>
#define SIZE 10
int main(void) {
char string[3][SIZE];
int i, j;
int c;
for(i = 0; i < 3; i++)
for(j = 0; j < SIZE; j++){
string[i][j] = 0;
}
printf("Enter three lines of text:\n");
for (i = 0; i < 3; i++) {
j = 0;
while ((j < SIZE) && ((c = getchar()) != '\n')) {
string[i][j] = c;
j++;
}
}
for (i = 0; i < 3; i++) {
for (j = 0; j < SIZE; j++) {
printf("%c", string[i][j]);
}
printf("\n");
}
return 0;
}
Also be aware that getchar() may behave lousy with input and newlines - it depends on whether you console buffers input before sending it to your program on enter(newline) or not. More here How to avoid press enter with any getchar()

Note: I wrote this answer before the OP clarified they had to use getchar.
To read a whole line at a time, use fgets. To print a whole string at a time, use printf with the %s format.
#include <stdio.h>
int main(void) {
// No need to define a SIZE constant.
// Because it's stack allocated we can its size.
char strings[3][100];
printf("Enter three lines of text:\n");
for ( int i = 0; i < 3; i++) {
// Reads one line, up to the size of `strings[i]`, from stdin.
fgets( strings[i], sizeof(strings[i]), stdin );
}
for ( int i = 0; i < 3; i++) {
// Print each string and its line number.
printf("Line %d: %s\n", i, strings[i]);
}
return 0;
}
This is not the best pattern to read input. You'll learn very quickly that fixed memory sizes and reading input don't work well. For future reference, it would be more like this.
#include <stdio.h>
#include <string.h>
int main(void) {
// A list to store 3 strings, but no memory for the strings themselves.
char *strings[3];
printf("Enter three lines of text:\n");
// A line buffer that's sufficiently large.
// This will be reused.
char line[4096];
for ( int i = 0; i < 3; i++) {
// Read into the large line buffer.
fgets( line, sizeof(line), stdin );
// Copy the string into a buffer that's just big enough.
strings[i] = strdup( line );
}
for ( int i = 0; i < 3; i++) {
printf("Line %d: %s\n", i, strings[i]);
}
return 0;
}
This allocates a single large line buffer to do the reading, then copies what its read with strdup to memory of just the right size. This lets you read even very long lines of input without wasting a bunch of memory if they're very short.
Note that strdup() is not part of the C standard library, but it's part of the POSIX spec. Any major compiler will have it, and it's easy to write your own.

Related

Why are there random characters outputting in my code?

I have been writing a program to input a phrase and turn it into an acronym. For some reason when I output my acronym at the moment it comes out with a bunch of random characters. How do I fix it?
#include <stdio.h>
#include <string.h>
#define MAXLEN 50
int main() {
int num;
printf("Enter number of acronyms to add to the database:");
scanf("%d", &num);
getchar();
char strings[num][MAXLEN];
char acronym[num][MAXLEN];
for(int i = 0; i < num; i++){
printf("Enter the string to convert into an acronym:");
fgets(strings[i],MAXLEN,stdin);
printf("%s\n", strings[i]);
for(int j = 0; j < 11; j++){
if((strings[i][j]) >= 'A' && (strings[i][j]) <= 'Z'){
char buffer[][20] = {strings[i][j]};
strcat(acronym[i], buffer[i]);
}
}
puts(acronym[i]);
}
return 0;
}
I have tried changing the MAXLEN value to see if it was a memory issue or like a buffer overload. I've also just tried changing around how the strings switch and work together but nothing has worked.
char buffer[][20] = {strings[i][j]};
Here you let the compiler count how many elements the array has from the initialization.
It has 1 element, A string with single a single character strings[i][j] and rest of the 20 byte array filled with 0.
strcat(acronym[i], buffer[i]);
Here you access buffer[i], but there is only one string there (as explained above), so this is invalid if i is anything but 0.
I'm not sure what you are trying to do, but this would be valid implementation of what this code tries to do:
// extract single character as a string
char buffer[2] = {strings[i][j], 0}; // only one of 2 and 0 is mandatory
// append it to acronym
strncat(acronym[i], 20, buffer);
Probably lots of other stuff there is wrong, but here is one definite issue and a possible solution.

The first char of my output is omitted in C

I can't seem to figure out what is going on with my output. I am reading in multiple lines of user input and outputting corresponding input that exceeds a lower boundary. For some reason when I output, the string that's outputted is omitting the first character of the string. Can anyone tell me why this is occuring?
#include <stdio.h>
typedef struct{
char name[4];
int population;
} state;
enum { MAX_STATES = 10 };
int main()
{
state myStates[MAX_STATES];
int c;
int i = 0;
while ((c = getchar())!= EOF)
{
scanf("%s %d\n", myStates[i].name, &myStates[i].population);
i++;
}
// printf("Last character is [%d]\n", c);
printf("");
if (c <= 0)
{
for(int j = 0; j <= MAX_STATES; j++)
{
if(myStates[j].population >= 10)
printf("%s %d\n", myStates[j].name, myStates[j].population);
else
break;
}
}
return 0;
}
Input:
TX 23
CA 45
Output:
X 23
A 45
Updated Code:
#include <stdio.h>
typedef struct{
char name[4];
int population;
} State;
enum { MAX_STATES = 10 };
int main()
{
State myStates[MAX_STATES];
int i, j;
// Function to read in multiple lines (up to 10) of user input; loop
// controls in place, detects format problems, prevents string buffer
// overflows.
for (i = 0; i < MAX_STATES; i++)
{
if (scanf("%2s %d\n", myStates[i].name, &myStates[i].population) != 2)
break;
}
// Function to output (stdout) array of State structs that exceed 10
// population.
for(j = 0; j < i; j++)
{
if(myStates[j].population >= 10)
printf("%s %d\n", myStates[j].name, myStates[j].population);
else
break;
}
return 0;
}
The output as posted, only goes until there is an input that is less than 10 and it breaks out of the loop. When I didn't have that break statement, I was getting garbage output at the last line. Any suggestions to improve the output?
Replace:
int i = 0;
while ((c = getchar()) != EOF)
{
scanf("%s %d\n", myStates[i].name, &myStates[i].population);
i++;
}
with:
int i;
for (i = 0; i < MAX_STATES; i++)
{
if (scanf("%3s %d", myStates[i].name, &myStates[i].population) != 2)
break;
}
This protects you against entering too many states, uses the for loop to put the loop controls in place, detects format problems, prevents string buffer overflows, and reads the first character into the name. Also, a trailing white space (such as a blank or newline) in a format string is a very bad idea in a scanf() format string if the input is being entered interactively. If the input comes from a file, it is less serious but still unnecessary most of the time. (See Trailing blank in scanf() format for more information.)
Keeping a while loop
If you're really adamant that you need a while loop, then you can use:
int i = 0;
while (i < MAX_STATES && (c = getchar()) != EOF)
{
ungetc(c, stdin);
if (scanf("%3s %d", myStates[i].name, &myStates[i].population) != 2)
break;
i++;
}
or:
int i = 0;
while (i < MAX_STATES && (c = getchar()) != EOF)
{
myStates[i].name[0] = c;
if (scanf("%2s %d", &myStates[i].name[1], &myStates[i].population) != 2)
break;
i++;
}
Note that these while loops still maintain both lots of overflow protection — overflowing the main array, and overflowing the name field. Note that one of the two scanf() statements uses %3s and the other %2s; you should be able to explain why. (And yes, the null byte is not counted by scanf(), so you have to use an 'off-by-one' length in the conversion specification.)
There are, no doubt, other techniques that could also be used. However, I think you'll find that the for loop is more nearly idiomatic C.
One alternative that is often sensible is to use fgets() (or POSIX getline() if it is available) to read whole lines, and then sscanf() to parse the lines. This often leads to more resilient programs, and better error reporting. It also stops people who try to put the information for all 50 states on a single line, or who put each datum on a separate line with a blank line in between them all, from getting away with the malformed data. You can quietly insist on two fields (and, if you're careful, only two fields) on the line.
And the output code?
May I inquire about a suggestion for displaying the output properly?
You have:
printf("");
if (c <= 0)
{
for(int j = 0; j <= MAX_STATES; j++)
{
if(myStates[j].population >= 10)
printf("%s %d\n", myStates[j].name, myStates[j].population);
else
break;
}
}
The first printf() does nothing; it should go. The if (c <= 0) condition is a bit dubious. It is possible to type a null byte (often Control-# or Control-Shift-2), though it would be a bit hard to get that to break the original loop. The for loop should be more like for (int j = 0; j < MAX_STATES; j++) — this is the template for safe for loops in C. You most frequently use for (int i = 0; i < MAX; i++). However, you only want to print the states that were read, so instead of using MAX_STATES, you need to use i as the limit. If you really only want to print the top 9 states (CA, TX, FL, NY, IL, PA, OH, GA, NC — see Wikipedia; Michigan is just shy of 10M, it says), then the if condition is fine.
So, you could use (noting that the input loop sets i to the number of states read successfully):
for (int j = 0; j < i; j++)
printf("State: %.2s, Pop'n: %dM\n", myStates[j].name, myStates[j].population);
You can tweak the format to suit your requirements, of course. This will print nothing if no states were read, or the number of states that were read. If you really want to apply the condition on the population, then you'd use:
for (int j = 0; j < i; j++)
{
if (myStates[i].population >= 10)
printf("State: %.2s, Pop'n: %dM\n", myStates[j].name, myStates[j].population);
}
An alternative is:
int i = 0;
char temp[100];
for(i=0; i<MAX_STATES; i++){
fgets(temp, 100, stdin);
if(strcmp(temp, "\n") == 0)
break;
sscanf(temp, "%s %d\n", myStates[i].name, &myStates[i].population);
}
You could try adding a space before %s in scanf, or specify a strict number of chars.
scanf(" %3s",
Or even use as in Beej's guide:
// read all whitespace, then store all characters up to a newline
scanf(" %[^\n]", s);
You could try adding a space before %s in scanf, or specify a strict number of chars.
Or even use this:
// read all whitespace, then store all characters up to a newline
scanf(" %[^\n]", s);

C - How to pass a char array to a function for sorting?

I am trying to compare strings in a mergesort algorithm after reading characters from a file as integers, and converting them to strings in an array. I am able to get the strings to print out, but when the char[] array is passed to the mergesort algorithm, the program crashes at the strcmp() step which is in the merge() step of the merge sort.
I tested to see that my temp char[] arrays are not initializing properly, so I think the problem is that I am not passing the original char[] array "charr" to the mergsort function.
I am lost as to how to do this. I borrowed the mergesort algorithm from the web, and it works on int arrays, but the simple change of changing int[] arrays to char[] arrays does not work.
How is it possible to get the char[] array that I want the sort done on to pass properly and initialize in the mergesort function?
The permutations look like this in the text file:
aaaab
aaaba
aabaa
abaaa
baaaa
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main(void) {
int arr[243][6];
//This is the array that I want to store my strings
char *charr[243][6];
int c, i = 0 , j = 0;
FILE *file;
file = fopen("permutations.txt", "r");
if (file) {
while ((c = getc(file)) != EOF) {
// we are reading each char in the string
//every time we hit a new line char (\n = 10)
//advance the array one, otherwise add the
// char
if (c != 10) {
arr[i][j] = c;
j++;
}
else {
arr[i][j] = c;
sprintf(charr[i], "%d%d%d%d%d%d", arr[i][0], arr[i][1],
arr[i][2], arr[i][3], arr[i][4]);
i++;
j = 0;
}
}
fclose(file);
}
if (strcmp(charr[0],charr[1]) < 0)
printf("less\n");
else
printf("other\n");
r_mergesort(charr,0,242);
for (int k = 0; k < 243; k++) {
printf(charr[k]);
for (int l = 0; l < 6; l++) {
putchar(arr[k][l]);
}
}
return 0;
}
/*l is for left index and r is right index of the sub-array*/
void r_mergesort (char arr[], int l, int r) {
//base case
if (l < r) {
//divide
int m = (l + r) /2;
// recursively sort halves
r_mergesort(arr, l, m);
r_mergesort(arr, m + 1, r);
// merge results
merge(arr, l, m, r);
}
}
void merge (char arr[], int l, int m, int r) {
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
// create temp arrays
char left[n1], right[n2];
// copy data to temp arrays
for (i = 0; i < n1; i++) {
left[i] = arr[l + i];
}
for (j = 0; j < n2; j++)
right[j] = arr[m + 1 + j];
// merge the temp arrays back into arr[]
i = 0;
j = 0;
k = l;
while (i < n1 && j < n2) {
if (strcmp(left[i], right[j]) < 0) {
arr[k] = left[i];
i++;
}
else {
arr[k] = right[j];
j++;
}
k++;
}
//copy the remaining elements of left[]
while (i < n1) {
arr[k] = left[i];
i++;
k++;
}
//copy the remaining elements of right[]
while (i < n2) {
arr[k] = right[j];
j++;
k++;
}
}
While there is nothing wrong with character-oriented input (e.g. getc) if, as you describe, your permutations.txt contains one possible permutation per-line, then using line-oriented input will simplify your read (which I suspect is where a bulk of your problem lies). So let's get you reading your data file properly as a start to solving your issues.
Using line-oriented input, your primary functions are fgets and getline. Each has certain pluses and minuses. Since you are dealing exclusively with static declarations, we will use fgets below for the example.
One thing to be aware of with line-oriented input, is that fgets will read until a newline ('\n') is encounter or the maximum number of characters specified (minus 1 leaving room for the nul-terminator). What this means in your case is, if you have declared charr[243][7] and have 6 chars per-line (plus the '\n' for a total of 7 chars) you will run into problems if you do not increase your string size by an additional character to allow the '\n' to be read as part of each line (and also providing space for the nul-terminator).
Basically what will happen is you will tell fgets to read a max of 7 chars which means it will read all 6 of your permutation characters, but leave the '\n' at the end of the line unread. Your next call to fgets will read only the '\n'. To solve the entire problem, simply declare charr[243][8] = {{0}}; to allow a complete read of each line.
You may say, 'that doesn't sound much simpler' -- it is, I just wanted to make sure and give a thorough explanation so you don't end up caught in a subtle issue of reading 1-less that the whole line. Of course, since all line-oriented input functions, read and include the '\n' as part of their read, you will want to remove the newline from the stings stored in the array. After the explanation, hopefully the example will make the read much clearer:
#include <stdio.h>
#include <string.h>
#define MAXR 243
#define MAXC 8
int main (int argc, char **argv) {
char charr[MAXR][MAXC] = {{0}};
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
size_t i = 0;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
return 1;
}
while (i < MAXR && fgets (charr[i], MAXC, fp))
{
/* get length, strip trailing newline */
size_t len = strlen (charr[i]);
if (charr[i][len-1] == '\n') charr[i][len-1] = 0;
printf (" charr[%zu] : %s\n", i, charr[i]);
i++;
}
if (fp != stdin) fclose (fp);
return 0;
}
The code above simply reads and prints (with the line index) each permutation read from the file given as the first argument to the program (or from stdin if no filename is given). It is just to confirm the read of your permutations.txt file to begin with.
Compile
gcc -Wall -Wextra -O3 -o bin/readperm readperm.c
Test Input (permutations.txt)
$ cat permutations.txt
123456
234561
345612
456123
Output
$ ./bin/readperm permutations.txt
charr[0] : 123456
charr[1] : 234561
charr[2] : 345612
charr[3] : 456123
While fgets and getline are the primary tools for line-oriented input, and I rarely recommend the scanf family of functions, if your permutations.txt file is exactly as you describe fscanf can be used very efficiently in this case. Generally, the choice of format-string and proper format-specifiers is what gives new C programmers fits. Since fscanf doesn't require that you read the newline, you can use the char charr[243][7] = {{0}}; declaration and not have to worry about removing the included newline. Specifically, you can replace the read loop above with:
while (i < MAXR && fscanf (fp, " %[^\n]%*c", charr[i]) == 1)
{
printf (" charr[%zu] : %s\n", i, charr[i]);
i++;
}
Note the choice of format specifier " %[^\n]%*c". The leading space between the opening " and '%' will skip any whitespace before the first character. The character case expression used as a format-specifier %[^\n] will read all characters up to, but not inlcuding, the newline. The assignment-suppression %*c will read and discard the '\n' without including it in your string (or in the match-count returned by fscanf).
note you could simply use a " %s" format specifier and accomplish the same read in your case, but that would have eliminated the explanation of the various parts of the format-string that is critical to understanding correct use of the scanf family of functions.
Lastly, note above the use of the return == 1. fscanf returns the number of successful conversions (according to the format-specifiers). Therefore you want to continue your read so long as fscanf makes a single conversion to string each time it is called. When it fails to make a proper conversion your read loop terminates (you can assign the return to a variable and check within the body of the loop to confirm EOF versus a read error)
Let me know when you get your read of permutations.txt sorted out and we will work on any lingering issues you have after confirming you have your read fixed.
try char *charr[243][6]; --> char charr[243][7];
char arr[] --> char arr[][7] The modified program you consider it
sprintf(charr[i], "%d%d%d%d%d%d" --> sprintf(charr[i], "%c%c%c%c%c%c"
– BLUEPIXY
The three changes you suggested were the ones that got me on the right track. I had to initialize then entire length of each string in the merge algorithm, it was not enough to say left[i] = arr[l + i].
– lefunction

Program runs too slowly with large input - C

The goal for this program is for it to count the number of instances that two consecutive letters are identical and print this number for every test case. The input can be up to 1,000,000 characters long (thus the size of the char array to hold the input). The website which has the coding challenge on it, however, states that the program times out at a 2s run-time. My question is, how can this program be optimized to process the data faster? Does the issue stem from the large char array?
Also: I get a compiler warning "assignment makes integer from pointer without a cast" for the line str[1000000] = "" What does this mean and how should it be handled instead?
Input:
number of test cases
strings of capital A's and B's
Output:
Number of duplicate letters next to each other for each test case, each on a new line.
Code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main() {
int n, c, a, results[10] = {};
char str[1000000];
scanf("%d", &n);
for (c = 0; c < n; c++) {
str[1000000] = "";
scanf("%s", str);
for (a = 0; a < (strlen(str)-1); a++) {
if (str[a] == str[a+1]) { results[c] += 1; }
}
}
for (c = 0; c < n; c++) {
printf("%d\n", results[c]);
}
return 0;
}
You don't need the line
str[1000000] = "";
scanf() adds a null terminator when it parses the input and writes it to str. This line is also writing beyond the end of the array, since the last element of the array is str[999999].
The reason you're getting the warning is because the type of str[10000000] is char, but the type of a string literal is char*.
To speed up the program, take the call to strlen() out of the loop.
size_t len = strlen(str)-1;
for (a = 0; a < len; a++) {
...
}
str[1000000] = "";
This does not do what you think it does and you're overflowing the buffer which results in undefined behaviour. An indexer's range is from 0 - sizeof(str) EXCLUSIVE. So you either add one to the
1000000 when initializing or use 999999 to access it instead. To get rid of the compiler warning and produce cleaner code use:
str[1000000] = '\0';
Or
str[999999] = '\0';
Depending on what you did to fix it.
As to optimizing, you should look at the assembly and go from there.
count the number of instances that two consecutive letters are identical and print this number for every test case
For efficiency, code needs a new approach as suggeted by #john bollinger & #molbdnilo
void ReportPairs(const char *str, size_t n) {
int previous = EOF;
unsigned long repeat = 0;
for (size_t i=0; i<n; i++) {
int ch = (unsigned char) str[i];
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
}
char *testcase1 = "test1122a33";
ReportPairs(testcase1, strlen(testcase1));
or directly from input and "each test case, each on a new line."
int ReportPairs2(FILE *inf) {
int previous = EOF;
unsigned long repeat = 0;
int ch;
for ((ch = fgetc(inf)) != '\n') {
if (ch == EOF) return ch;
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
return ch;
}
while (ReportPairs2(stdin) != EOF);
Unclear how OP wants to count "AAAA" as 2 or 3. This code counts it as 3.
One way to dramatically improve the run-time for your code is to limit the number of times you read from stdin. (basically process input in bigger chunks). You can do this a number of way, but probably one of the most efficient would be with fread. Even reading in 8-byte chunks can provide a big improvement over reading a character at a time. One example of such an implementation considering capital letters [A-Z] only would be:
#include <stdio.h>
#define RSIZE 8
int main (void) {
char qword[RSIZE] = {0};
char last = 0;
size_t i = 0;
size_t nchr = 0;
size_t dcount = 0;
/* read up to 8-bytes at a time */
while ((nchr = fread (qword, sizeof *qword, RSIZE, stdin)))
{ /* compare each byte to byte before */
for (i = 1; i < nchr && qword[i] && qword[i] != '\n'; i++)
{ /* if not [A-Z] continue, else compare */
if (qword[i-1] < 'A' || qword[i-1] > 'Z') continue;
if (i == 1 && last == qword[i-1]) dcount++;
if (qword[i-1] == qword[i]) dcount++;
}
last = qword[i-1]; /* save last for comparison w/next */
}
printf ("\n sequential duplicated characters [A-Z] : %zu\n\n",
dcount);
return 0;
}
Output/Time with 868789 chars
$ time ./bin/find_dup_digits <dat/d434839c-d-input-d4340a6.txt
sequential duplicated characters [A-Z] : 434893
real 0m0.024s
user 0m0.017s
sys 0m0.005s
Note: the string was actually a string of '0's and '1's run with a modified test of if (qword[i-1] < '0' || qword[i-1] > '9') continue; rather than the test for [A-Z]...continue, but your results with 'A's and 'B's should be virtually identical. 1000000 would still be significantly under .1 seconds. You can play with the RSIZE value to see if there is any benefit to reading a larger (suggested 'power of 2') size of characters. (note: this counts AAAA as 3) Hope this helps.

Scanf and two strings

My task is read two strings of digits and save them in different arrays.
I decided to use scanf function, but program can read only first string.
This is my bad-code.
int main()
{
int firstArray[50], secondArray[50], i, j;
/* fill an array with 0 */
for(i=0; i<50; ++i)
{
firstArray[i]=secondArray[i]=0;
}
i=j=0;
while((scanf("%d", &firstArray[i]))== 1) { ++i; }
while((scanf("%d", &secondArray[j]))== 1) { ++j; }
/* Print this. */
for(i = 0; i < 20; ++i)
{
printf("%d ", firstArray[i]);
}
putchar('\n');
for(j = 0; j < 20; ++j)
{
printf("%d ", secondArray[j]);
}
return 0;
}
I just don't understand how scanf function works. Can someone please explain?
scanf ignores blank characters (including new line). Thus your scan will read entire input into firstArray if you have no "non blank" separator.
If file/data has ; at end of first line it will stop the read into firstArray there, and never read anything into secondArray - as you never consume the ;.
/* This will never be 1 as ; is blocking */
while((scanf("%d", &secondArray[i])) == 1) {
So: if you separate with i.e. ; you will have to read / check for this before you read into secondArray.
You could also add something like:
char c;
/* this can be done more tidy, but only as concept */
while((scanf("%d", &firstArray[i])) == 1 && i < max) {
++i;
if ((c = getchar()) == '\n' || c == ';')
break;
}
Also instead of initializing array to 0 by loop you can say:
int firstArray[50] = {0}; /* This set every item to 0 */
Also take notice to ensure you do not go over your 50 limit.
You say strings of digits and you read %d. The format scans the input for the longest sequence representing an integer (signed) value. Two "digit strings" are consumed by the first while loop.
EDIT Instead of "strings of digits" you should say "strings of integers". In this case it is a little bit more subtle since the first while can consume all the integers, unless they are separated by something that is not a possible integer (e.g. a ;).
So, to make the following to work, you must separate the two "lines" with something that can't be parsed as integer and which is not considered "white character". Not the better solution, but one the possible.
#include <stdio.h>
#include <ctype.h>
int main()
{
int firstArray[50] = {0};
int secondArray[50] = {0};
int i, j, l1, l2;
int tmp;
i = j = 0;
// read integers, but not more than size of array
while( scanf("%d", &firstArray[i]) == 1 && i < sizeof(firstArray) ) {
++i;
}
// consume non digits
for(tmp = getchar(); tmp != EOF && !isdigit(tmp); tmp = getchar());
// on EOF you should exit and stop processing;
// we read one more char, push it back if it was a digit
if (isdigit(tmp)) ungetc(tmp, stdin);
while( scanf("%d", &secondArray[j]) == 1 && j < sizeof(secondArray) ) {
++j;
}
l1 = i; // preserve how many ints were read
l2 = j;
/* Print this. */
for(i = 0; i < l1; ++i)
{
printf("%d ", firstArray[i]);
}
putchar('\n');
for(j=0; j < l2; ++j)
{
printf("%d ", secondArray[j]);
}
return 0;
}
EDIT A solution that maybe fits your need better is to read the lines (one per time) into a buffer and sscanf the buffer.
You cannot use scanf to do that.
Read the documentation.
Observations:
with scanf if you enter a digit your loop runs forever
there is no check on size 50 limit of your arrays
if you press return then it ignores that line because does not match your pattern
if you enter a letter the pattern does not match and loop breaks
So use some other function, maybe gets, atoi or strtol. And remember to check the size 50 limit of your arrays.
Actually, there is one special point in C's arrays.
Though you declare an array's size. say int arr[5]; You can store values beyond the size of 5. It doesn't show any error but leads to undefined behavior (Might overwrite other variables).
Please Refer this question: Array size less than the no. of elements stored in it
In you case, that was your problem. The compiler had never passed beyond the first while statements. Thus, you didn't get any output. In fact, it didn't even compile the whole code yet!
while((scanf("%d", &firstArray[i]))== 1) { ++i; }
So, you could write this while statement like this:
while( scanf("%d", &firstArray[i]) ==1 && i<50 )
i++;
or else:
while(i<50 )
{
scanf("%d", &firstArray[i]);
i++;
}
or else:
for (i=0; i<50; i++)
scanf("%d", &firstArray[i]);

Resources