C: print longest string in a dynamic allocated array of chars - arrays

I've made a dynamic allocated array of chars and read some lines of text from a .txt file. How can I find the longest "string" in the array?
The .txt file looks like this:
usr
user
username
somerandomtext
Here's my code that loads the array and prints it:
char c = fgetc(rezultati);
printf("\n");
int x = 0;
while (c != EOF){
pogg[x++] = c;
c = fgetc(rezultati);
}
pogg[x] = '\0';
printf("%s\n\n", pogg);
I have tried using qsort with a custom comparator function buy my output is just lines of ^2 (squared).

Read the file line by line using fgets(), and get the length of the line. Save the longest line in another variable.
You don't need an array of all the lines for this.
#define MAXLEN 200
char buffer[MAXLEN], longest[MAXLEN] = "";
size_t maxlength = 0;
while(fgets(buffer, MAXLEN, stdin) {
if (strlen(buffer) > maxlength) {
strcpy(longest, buffer);
}
}
printf("Longest line = %s\n", longest);

I think you need to use the function "strlen" You will then store the lengths in an array and compare them.
maxlen = arrayOfLengths[0];
while (i !=0) {
len = strlen(arrayOflengths[i]);
if (len > maxlen) {
maxlen = len;
maxindex = i;}
This has been little bit pseudocode-ish but still, I think that it will give you some idea.

Related

C programming: get line, 2D array and ending user input

char* str = NULL;
size_t capacity = 0;
getline(&str, &capacity, stdin);
The above code is an example of using getline to dynamically allocate memory while reading a string input. But, what if I'm trying to read the input into a 2D array?
Example:
Linenumberone (enter)
Linenumbertwo (enter)
(enter) <<< enter on an empty line - stop reading user input
I do know about the function strlen, so I was thinking I could technically use that to figure out when to stop reading user input? But I'm a bit confused, is it even possible to read user input using getline into a 2D array in C as described? I've only seen people using it in C++
We can declare an array of pointers, then assign each line to the 2D array in a loop. See the code below:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *line[5] = {NULL}; // array of 5 pointers
size_t len = 0;
int i=0;
for(i=0;i<5;i++)
{
getline(&line[i],&len, stdin); // reading strings
}
printf("\nThe strings are \n");
for(int i=0; i<5; i++)
{
printf("%s",line[i]); // prinitng the strings.
}
return 0;
}
The output is (first 5 lines are input) :
krishna
rachita
teja
sindhu
sagarika
The strings are
krishna
rachita
teja
sindhu
sagarika
Each time characters = getline(&str,...) is used, new dynamic memory is allocated at the address str, with a size equal to number of characters read. It suffices to store the buffer address (value of str) into an array at each getline() call. Subsequently, the buffer address (str) in getline() is incremented by the number of characters read in the last getine(). See code and example below.
#include <stdio.h>
int main () {
char *buffer=NULL;
size_t capacity = 0;
size_t maxlines = 100;
char *lines[maxlines]; // pointers into the malloc buffer for each line
printf ("Input\n\n");
int lines_read;
int characters;
// read getline until empty line or maxlines
for (lines_read = 0; lines_read < maxlines; lines_read++) {
printf ("Enter line %d: ", lines_read + 1);
characters = getline (&buffer, &capacity, stdin);
// stop at empty line
if (characters == 1) break;
// convert end of line "\n" into zero (C null-terminated string convention)
buffer[characters - 1] = 0;
// save starting location into lines
lines[lines_read] = buffer; // save pointer to the start of this line in the buffer
buffer += characters; // set pointer to the start of a new line in the buffer
}
printf ("\nOutput\n\n");
// print lines read excluding empty line
for (int i = 0; i < lines_read; i++)
printf ("Line[%d] = %s\n", i+1, lines[i]);
return (0);
}
Example output:
Input
Enter line 1: This
Enter line 2: is
Enter line 3: an
Enter line 4: example.
Enter line 5:
Output
Line[1] = This
Line[2] = is
Line[3] = an
Line[4] = example.

How to restore string after using strtok()

I have a project in which I need to sort multiple lines of text based on the second, third, etc word in each line, not the first word. For example,
this line is first
but this line is second
finally there is this line
and you choose to sort by the second word, it would turn into
this line is first
finally there is this line
but this line is second
(since line is before there is before this)
I have a pointer to a char array that contains each line. So far what I've done is use strtok() to split each line up to the second word, but that changes the entire string to just that word and stores it in my array. My code for the tokenize bit looks like this:
for (i = 0; i < numLines; i++) {
char* token = strtok(labels[i], " ");
token = strtok(NULL, " ");
labels[i] = token;
}
This would give me the second word in each line, since I called strtok twice. Then I sort those words. (line, this, there) However, I need to put the string back together in it's original form. I'm aware that strtok turns the tokens into '\0', but Ive yet to find a way to get the original string back.
I'm sure the answer lies in using pointers, but I'm confused what exactly I need to do next.
I should mention I'm reading in the lines from an input file as shown:
for (i = 0; i < numLines && fgets(buffer, sizeof(buffer), fp) != 0; i++) {
labels[i] = strdup(buffer);
Edit: my find_offset method
size_t find_offset(const char *s, int n) {
size_t len;
while (n > 0) {
len = strspn(s, " ");
s += len;
}
return len;
}
Edit 2: The relevant code used to sort
//Getting the line and offset
for (i = 0; i < numLines && fgets(buffer, sizeof(buffer), fp) != 0; i++) {
labels[i].line = strdup(buffer);
labels[i].offset = find_offset(labels[i].line, nth);
}
int n = sizeof(labels) / sizeof(labels[0]);
qsort(labels, n, sizeof(*labels), myCompare);
for (i = 0; i < numLines; i++)
printf("%d: %s", i, labels[i].line); //Print the sorted lines
int myCompare(const void* a, const void* b) { //Compare function
xline *xlineA = (xline *)a;
xline *xlineB = (xline *)b;
return strcmp(xlineA->line + xlineA->offset, xlineB->line + xlineB->offset);
}
Perhaps rather than mess with strtok(), use strspn(), strcspn() to parse the string for tokens. Then the original string can even be const.
#include <stdio.h>
#include <string.h>
int main(void) {
const char str[] = "this line is first";
const char *s = str;
while (*(s += strspn(s, " ")) != '\0') {
size_t len = strcspn(s, " ");
// Instead of printing, use the nth parsed token for key sorting
printf("<%.*s>\n", (int) len, s);
s += len;
}
}
Output
<this>
<line>
<is>
<first>
Or
Do not sort lines.
Sort structures
typedef struct {
char *line;
size_t offset;
} xline;
Pseudo code
int fcmp(a, b) {
return strcmp(a->line + a->offset, b->line + b->offset);
}
size_t find_offset_of_nth_word(const char *s, n) {
while (n > 0) {
use strspn(), strcspn() like above
}
}
main() {
int nth = ...;
xline labels[numLines];
for (i = 0; i < numLines && fgets(buffer, sizeof(buffer), fp) != 0; i++) {
labels[i].line = strdup(buffer);
labels[i].offset = find_offset_of_nth_word(nth);
}
qsort(labels, i, sizeof *labels, fcmp);
}
Or
After reading each line, find the nth token with strspn(), strcspn() and the reform the line from "aaa bbb ccc ddd \n" to "ccd ddd \naaa bbb ", sort and then later re-order the line.
In all case, do not use strtok() - too much information lost.
I need to put the string back together in it's original form. I'm aware that strtok turns the tokens into '\0', but Ive yet to find a way to get the original string back.
Far better would be to avoid damaging the original strings in the first place if you want to keep them, and especially to avoid losing the pointers to them. Provided that it is safe to assume that there are at least three words in each line and that the second is separated from the first and third by exactly one space on each side, you could undo strtok()'s replacement of delimiters with string terminators. However, there is no safe or reliable way to recover the start of the overall string once you lose it.
I suggest creating an auxiliary array in which you record information about the second word of each sentence -- obtained without damaging the original sentences -- and then co-sorting the auxiliary array and sentence array. The information to be recorded in the aux array could be a copy of the second word of the sentence, their offsets and lengths, or something similar.

how to save a string token , save its content to an array, then use those contents for further comparison

/*I am unsure if my code for saving the tokens in an array is accurate.
This is so because been whenever I run my program, the code to compare
token[0] with my variable doesn't give an output nor perform assigned function.
Hence I am sure there is something inaccurate about my coding.*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
//variable declarations
const char *array[] = {"ax","bo","cf"};
char delim[]=" \n";
char* myline;
size_t max = 500;
char* token1;
char* token2[max];
int n = 0;
while(1) //loop always
{
printf("Enter an argument\n"); //asks for an input
getline (&myline, &max, stdin); //read the input/line
//for loop -- splits up the line into tokens
for(token1 = strtok(myline, " "); token1 != NULL; token1 = strtok(NULL, delim))
{
token2[n] = malloc(strlen(token1)+1); //allocate some space/memory to token2[n]
//save the token in an array by copying from token1 to token2
strcpy(token2[n],token1);
int m;
for(m = 0; m<sizeof(array);m++) //loop through the array elements for comparison
{
//compare array at index m with token at index 0 -- compare only first token with a specific variable
if(strcmp(token2[0], array[m]) == 0)
{
printf("equal");
}
}
}
free(token2[n]); //deallocate assigned memory
}
return(0);
}
I think you should try vector of string like
vector < string > str = { "ax","bo","cf" };
Their seems to be a few issues in your current code:
for(m = 0; m<strlen;m++) is not correct. strlen() is a <string.h> function used to obtain the length of a C string. Since you want array[i], you need to include the size of array in the guard. To find the size of the array, you can use sizeof(array)/sizeof(array[0]). It would be good to include this in a macro:
#define ARRAYSIZE(x) (sizeof x/sizeof x[0])
Then your loop can be:
size_t m;
for(m = 0; m<ARRAYSIZE(array); m++)
You need to check return of malloc(), as it can return NULL on failure to allocate spaces. Here is a way to check this:
token2[n] = malloc(strlen(token1)+1);
if (token2[n] == NULL) {
/* handle error */
It is possible to skip the malloc()/strcpy() step by simply using strdup.
getline() returns -1 on failure to read a line, so its good to check this. It also adds a \n character at the end of the buffer, so you need to remove this. Otherwise, strcmp will never find equal strings, as you will be comparing strcmp("string\n", "string"). You need to find the \n character in your buffer, and replace it with a \0 null-terminator.
You can achieve this like:
size_t slen = strlen(myline);
if (slen > 0 && myline[slen-1] == '\n') {
myline[slen-1] = '\0';
}
You also need to free() all of the char* pointers in token2[].
Since you are using the same delimeter for strtok(), its better to make this const. So const char *delim = " \n"; instead.
Alot of the fixes I suggested in the comments, so I didn't post them here, as you seemed to have updated your code with those suggestions.

small C program doesnt work

I tried to make a program that gets a user input(lines) and prints the longest line that is over 80 characters long. I made the program , but when i ran it , it outputed some very weird symbols. Could you please tell me what might be wrong with my code ?
#include <stdio.h>
#define MINLINE 80
#define MAXLINE 1000
int getline(char current[]);
void copy(char from[], char to[]);
int main(void)
{
int len; // current input line lenght
int max; // the lenght of a longest line that's over 80 characters
char current[MAXLINE]; // current input line
char over80[MAXLINE]; // input line that's over 80 characters long
while (len = (getline(current)) > 0) {
if (len > MINLINE) {
max = len;
copy(current, over80);
}
}
if (max > 0) {
printf("%s", over80);
}
else {
printf("No input line was over 80 characters long");
}
return 0;
}
int getline(char current[]) {
int i = 0, c;
while (((c = getchar()) != EOF) && c != '\n') {
current[i] = c;
++i;
}
if (i == '\n') {
current[i] = c;
++i;
}
current[i] = '\0';
return i;
}
void copy(char from[], char to[]) {
int i = 0;
while ((to[i] = from[i]) != '\0') {
++i;
}
}
Thank you very much for your help !
max can be not initialized if no long line is found. Using it in if (max > 0) is then undefined behavior.
This line:
while (len = (getline(current)) > 0) {
assigns the value of (getline(current)) > 0) to len, which is not what you want (len will be 0 or 1 afterwards.
EDIT: Just saw AusCBloke's comment, you should also check for both len > max and len > MINLINE or you'll just get the latest line longer than 80 chars, not the longest overall line.
You should also initialize max to 0, so it should be
max = 0;
while ((len = getline(current)) > 0) {
if ((len > MINLINE) && (len > max)) {
Other minor errors/tips:
The built in functions strcpy and strncpy do what your copy function does, there's no need to reinvent the wheel.
In your getline function, use MAXLINE to prevent buffer overflows.
Assuming that this is a homework, here's a hint: this piece of code looks very suspicious:
if (i == '\n') {
current[i] = c;
++i;
}
Since i represents a position and is never assigned a character, you are effectively checking if the position is equal to the ASCII code of '\n'.
Your copy method doesn't null terminate the string:
void copy(char from[], char to[]) {
int i = 0;
while ((to[i] = from[i]) != '\0') {
++i;
}
to[i] = '\0'
}
which probably explains the weird characters being printed.
You could use the builtin strcpy() to make life easier.
I can't test your code right now, but it may be caused by character arrays not being cleaned. Try memset-ing the char arrays to 0.
If you supply input data that has lines with more than 1000 characters you will overflow your fixed size buffers. By feeding in such input I was able to achieve the following output:
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
There are a number of problems with your code. Mostly they are due to wheel-reinvention.
int getline(char current[]);
You don't need to define your own, getline(), there is already one in stdio.h.
void copy(char from[], char to[]);
There are also a number of functions for copying strings in string.h.
It's also a good idea to initialise all 0f your variables, like this:
int len = 0; // current input line length
...this can prevent problems later, like comparisons to max when you haven't initialised it.
If you initialise max like this...
int max = MINLINE; // the length of a longest line that's over 80 characters
...then it's easier to do the length comparison later on.
char* current = NULL;
size_t allocated = 0;
If current is NULL, then getline() will allocate a buffer for storing the line, which should be freed by the user program. getline() also takes a pointer to a size_t, which contains the amount of bytes needed to store the line.
while (len = (getline(current)) > 0) {
Should be replaced by the following...
while ((len = getline(&current, &allocated, stdin)) > 0) {
...which updates and compares len to 0.
Now, instead of...
if (len > MINLINE) {
...you need to compare with the last longest line, which we initialised earlier...
if (len > max) {
...and then you're good to update max as you were...
max = len;
Where you called your copy() use strncpy(), which will prevent you writing over 1,000 characters into the allocated buffer:
strncpy(over80, current, MAXLINE);
Because we initialised max, you'll need to change your check at the end from if (max > 0) to if (max > MINLINE).
One more tip, changing the following line...
printf("No input line was over 80 characters long");
...to...
printf("No input line was over %d characters long", MINLINE);
...will mean that you only have to change the #define at the top of the file to increase or decrease the minimum length.
Don't forget to...
free(current);
...to prevent memory leaks!

Parsing text in C

I have a file like this:
...
words 13
more words 21
even more words 4
...
(General format is a string of non-digits, then a space, then any number of digits and a newline)
and I'd like to parse every line, putting the words into one field of the structure, and the number into the other. Right now I am using an ugly hack of reading the line while the chars are not numbers, then reading the rest. I believe there's a clearer way.
Edit: You can use pNum-buf to get the length of the alphabetical part of the string, and use strncpy() to copy that into another buffer. Be sure to add a '\0' to the end of the destination buffer. I would insert this code before the pNum++.
int len = pNum-buf;
strncpy(newBuf, buf, len-1);
newBuf[len] = '\0';
You could read the entire line into a buffer and then use:
char *pNum;
if (pNum = strrchr(buf, ' ')) {
pNum++;
}
to get a pointer to the number field.
fscanf(file, "%s %d", word, &value);
This gets the values directly into a string and an integer, and copes with variations in whitespace and numerical formats, etc.
Edit
Ooops, I forgot that you had spaces between the words.
In that case, I'd do the following. (Note that it truncates the original text in 'line')
// Scan to find the last space in the line
char *p = line;
char *lastSpace = null;
while(*p != '\0')
{
if (*p == ' ')
lastSpace = p;
p++;
}
if (lastSpace == null)
return("parse error");
// Replace the last space in the line with a NUL
*lastSpace = '\0';
// Advance past the NUL to the first character of the number field
lastSpace++;
char *word = text;
int number = atoi(lastSpace);
You can solve this using stdlib functions, but the above is likely to be more efficient as you're only searching for the characters you are interested in.
Given the description, I think I'd use a variant of this (now tested) C99 code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
struct word_number
{
char word[128];
long number;
};
int read_word_number(FILE *fp, struct word_number *wnp)
{
char buffer[140];
if (fgets(buffer, sizeof(buffer), fp) == 0)
return EOF;
size_t len = strlen(buffer);
if (buffer[len-1] != '\n') // Error if line too long to fit
return EOF;
buffer[--len] = '\0';
char *num = &buffer[len-1];
while (num > buffer && !isspace((unsigned char)*num))
num--;
if (num == buffer) // No space in input data
return EOF;
char *end;
wnp->number = strtol(num+1, &end, 0);
if (*end != '\0') // Invalid number as last word on line
return EOF;
*num = '\0';
if (num - buffer >= sizeof(wnp->word)) // Non-number part too long
return EOF;
memcpy(wnp->word, buffer, num - buffer);
return(0);
}
int main(void)
{
struct word_number wn;
while (read_word_number(stdin, &wn) != EOF)
printf("Word <<%s>> Number %ld\n", wn.word, wn.number);
return(0);
}
You could improve the error reporting by returning different values for different problems.
You could make it work with dynamically allocated memory for the word portion of the lines.
You could make it work with longer lines than I allow.
You could scan backwards over digits instead of non-spaces - but this allows the user to write "abc 0x123" and the hex value is handled correctly.
You might prefer to ensure there are no digits in the word part; this code does not care.
You could try using strtok() to tokenize each line, and then check whether each token is a number or a word (a fairly trivial check once you have the token string - just look at the first character of the token).
Assuming that the number is immediately followed by '\n'.
you can read each line to chars buffer, use sscanf("%d") on the entire line to get the number, and then calculate the number of chars that this number takes at the end of the text string.
Depending on how complex your strings become you may want to use the PCRE library. At least that way you can compile a perl'ish regular expression to split your lines. It may be overkill though.
Given the description, here's what I'd do: read each line as a single string using fgets() (making sure the target buffer is large enough), then split the line using strtok(). To determine if each token is a word or a number, I'd use strtol() to attempt the conversion and check the error condition. Example:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
* Read the next line from the file, splitting the tokens into
* multiple strings and a single integer. Assumes input lines
* never exceed MAX_LINE_LENGTH and each individual string never
* exceeds MAX_STR_SIZE. Otherwise things get a little more
* interesting. Also assumes that the integer is the last
* thing on each line.
*/
int getNextLine(FILE *in, char (*strs)[MAX_STR_SIZE], int *numStrings, int *value)
{
char buffer[MAX_LINE_LENGTH];
int rval = 1;
if (fgets(buffer, buffer, sizeof buffer))
{
char *token = strtok(buffer, " ");
*numStrings = 0;
while (token)
{
char *chk;
*value = (int) strtol(token, &chk, 10);
if (*chk != 0 && *chk != '\n')
{
strcpy(strs[(*numStrings)++], token);
}
token = strtok(NULL, " ");
}
}
else
{
/**
* fgets() hit either EOF or error; either way return 0
*/
rval = 0;
}
return rval;
}
/**
* sample main
*/
int main(void)
{
FILE *input;
char strings[MAX_NUM_STRINGS][MAX_STRING_LENGTH];
int numStrings;
int value;
input = fopen("datafile.txt", "r");
if (input)
{
while (getNextLine(input, &strings, &numStrings, &value))
{
/**
* Do something with strings and value here
*/
}
fclose(input);
}
return 0;
}

Resources