I just posted this question, but I made quite a few mistakes in the way that I copied my code--so I'm going to repost it.
I'm trying to recreate a head command which prints the top of a file to standard output. I'm not sure if the command can take more options than -n (user specifies number of lines they want to be printed) and -c (user specifies number of chars they want to be printed), but those are the options I am implementing. I've finished most of the code, but can't quite get my program to print the correct number of lines when the -n option is used. Here's my code for the -n option:
char *buff = malloc(1024);
ssize_t szt = 0;
lines_read = 0;
fd = open(file.txt, O_RDONLY); // placeholder file.txt
while ((szt = read(fd, buff, sizeof(buff))) > 0) {
for (int j = 0; j < sizeof(buff); ++j) {
if (buff[j] == '\n') {
++lines_read;
if (lines_read > n) {
write(STDOUT_FILENO, buff, j);
break;
} // if
} // if
} // for
} // for
Here's a link to my entire file head.c file. head
I see one problem here (may be more). The sizeof operator
[y]ields the size in bytes of the object representation of the type of expression, if that expression is evaluated. [emphasis mine]
Therefore, sizeof(buff) returns always the size of char *, not taking into account the size of allocated memory for buff. This might be not what you expect. See also Newbie questions about malloc and sizeof.
Related
The text file that I have contains the below as the data (Starting with the K and D and P respectively as the ID)
K1234:Green_Book:A_green_book:10:
K3346:Red_Book:A_red_book:7:
D3333:Grey_Book:A_grey_book:15:
D1111:Black_Book:A_black_book:1:
P0000:White_Book:A_white_book:6:
what I would like to do is to modify the Data in the line that starts with D3333 and change the value from 15 to 17 in the text file. I really do not know how to do that as I am new to C programming and this has been troubling me for days now. I have tied searching all over the net but my searches were of no avail. If anyone can please help me with the code that can do that or something similar, I would really appreciate it. Thanks.
This is what i have done so far:
void show(){
FILE * fl;
long fl_size;
char * buffer;
size_t res;
fl = fopen("inventItems.txt", "r+");
if (fl == NULL) {
fprintf(stderr, "File error\n");
_getch();
exit(1);
}
fseek(fl, 0, SEEK_END);
fl_size = ftell(fl);
rewind(fl);
buffer = (char*)malloc(sizeof(char)*fl_size);
if (buffer == NULL) {
fputs("Memory error", stderr);
_getch();
exit(2);
}
res = fread(buffer, 1, fl_size, fl);
if (res != fl_size) {
fputs("Reading error", stderr);
_getch();
exit(3);
}
char * strtok_res;
strtok_res = strtok(buffer, ":");
while (strtok_res != NULL)
{
printf("%s\n", strtok_res);//this prints the values from the file to a new line when i test it
//however i DO NOT KNOW how to modify and save it back onto the text file
strtok_res = strtok(NULL, ":");
_getch();
}
_getch();
fclose(fl);
free(buffer);
}
Ok, looks like you are doing this along the right lines. A couple of comments:
Repitition and Readability
You are repeating yourself a lot; variations on
if(whateverPointer == NULL) {
fprintf("My error message", stderr);
_getch();
exit(2);
}
are used on three separate occasions; so should be moved into a separate function that takes a char* (string) message to pass into fprintf(), so that you can just do
if(whateverPointer == NULL) {
errorThenDeath("My message", 2);
}
This might seem irrelavent to the question, but as well as being a good habit to get into, it will make your code easier to read. The easier your code is to read, the easier it is for other people to help you when you are stuck: always remember this.
On a similar veign, you have indicated that you want to use a specific identifier to change a specific line. With this in mind, you should be breaking your problem up to identify this specific problem, so the code that performs that particular task should be contained in a function that might have a signature like this.
int changeLineValue(char** buffer, size_t size, char* identifier, int newValue)
{
...
}
Note the double pointer (char**) this is a pointer to the original char*. When inside this function, you can get at the original pointer by dereferencing it (*) so
*buffer = "hello";
Would change the buffer to the string 'hello'.
The Problem
I'm not sure tokenising the buffer is the right approach. strtok actually writes in \0 characters at the end of each token, which will make it a pain when you try to write this back to a file. Another thing to note; because you don't know how many digits your new number might have, you may have to resize the buffer to compensate. The function signature assumes this is done, and returns a number representing how many characters were added or removed (if any).
My general approach is to iterate through each character looking for newlines. Each time you've found one, check to see if it is the correct line, if it is, get the value, determine whether you need to resize the buffer and do so if necessary. Once that has been done, change the value.
int changeLineValue(char** buffer, size_t size, char* identifier, int newValue)
for(int i = 0; i < size; i++) {
if(buffer[i] == '\n' && isCorrectLine(*buffer, size, i)) {
int oldVal = getCurrentValue(*buffer, size, i);
int resize = getDigitsDifference(oldVal, value);
if(resize != 0) {
resizeBuffer(buffer, resize);
}
modifyValueInPosition(buffer, fl_size, i, value);
return resize;
}
}
}
Note; because of the way that the C language works, isCorrectLine(...) will only be called if buffer[i] == \n evaluates to true. This is called short circuit evaluation.
Obviously the code above calls a couple of functions that have not yet been created, so part of your task will be to implement them. Note that the second parameter which has been passed fl_size is defined as size_t not long even though fl_size is a long. You should change this variable to size_t.
Below I have provided the function signatures. Since you are trying to learn C, I am not going to implement them for you.
This function will be called each time a newwline is encountered, with 'index' set to the position of that newline. It should return a 0 if the identifier is not found at this position or a 1 if it HAS beenfound at this position. Do not change anything in the buffer at this point
int isCorrectLine(char* buffer, char* identifier, size_t size, int index) {
}
This function should iterate along the line and return the number before the next newline character ('\n')
int getCurrentValue(char* buffer, size_t fl_size, i) {
}
This number should return the difference between how many digits each number has. E.g. 1 = 1 digit, 324 = 3 digits so 3 - 1 = 2
int digitsDifference(int old, int new) {
}
This function takes a double pointer (char**) and reallocates the memory with a larger or smaller buffer to take into account the different number of digits if required.
void resizeBuffer(char** buffer, int resize) {
*buffer = // This is how you change the original pointer. Line is still incomplete
}
Now the buffer is the correct size, you can go ahead and change the value in the buffer. Again, this function is passed in the position of the newline before the correct line, so you need to iterate along the line, change the values in position. If you find yourself overwriting a newline character (because the new number is longer), you might need to move all of the characters after this value along
int modifyValueInPosition(char* buffer, size_t fl_size, int index) {
}
Writing to file
Once you have changed the original buffer, writing it back to file is fairly easy. Your main function should look something like this now
int main() {
// ...code that gets buffer from file. Note, make sure you close the file handle
// afterwards it isn't good practise to leave a file handle open
fl_size += changeLineValue(buffer, fl_size, identifier, newValue);
// ...Reopen file handle as you did before ...
fwrite(buffer, sizeof(char), fl_size, fileHandle);
// ...Close file handle...
}
I have a problem which may look like if I copied my homework, but it's not my homework. It was part of a Test at University and I want to solve it (as well as others) at home so that I am prepared for the next Test.
My goal here is that I understand so that I can solve similar questions on my own. I am familiar with high-level languages but C is one of my weaknesses, this is why I have problems here.
The Question
Given the following method:
int safe_read(int fd, char *buff, size_t len){
do {
errno = 0;
pos += read(fd, buff + pos, len);
if (-1 == len) {
if (ENTER != errno) {
return 0;
}
}
} while(ENTER == errno);
return pos;
}
and the following call:
pos = safe_read(STDIN_FILENO, msg, 225);
Analyse the code and answer the following questions:
1) Does safe-read return the number of bytes read correctly in all cases?
2) If not, how can this be fixed?
Well. For now, I already understood the following:
1)
No it does not, as for the following reasons:
-the caller may set fd to something invalid.
-pos is not properly initialized.
-The variable ENTER is also not initialized.
-if(-1==len) does not make sense as len is a parameter set by the caller, thus will alsways be true or false at one call.
-it is also not safe as it is possible to go beyond the buffer's maximum size (by setting len to a value >= sizeof(buff))
-it does not return the number of characters read in all cases, as f.e. when I read len characters the first time, And the second time it fails. I will then return zero even though len characters have already been read.
2) Here is my fix.
initialize variables
/*
For a better understanding, I write what I understood about what this method is supposed to do:
-reads characters into char* buff.
-returns the number of characters read as int
-fd is a file descriptor of the file to read
-len is the number of bytes to be read
*/
int safe_read(int fd, char *buff, size_t len){
int ENTER=0;
int pos=0;
do {
errno = 0;
pos += read(fd, buff + pos, len);
if (len < 0) {
if (ENTER != errno) {
return 0;
}
}
} while(ENTER == errno);
return pos;
}
Did I understand you correctly? Is my fix correct?
Thank you!
Special thanks to Paul Ogilvie for the help before my edit!
Your code contains many errors and other members wonder if this is homework. But I'll attempt to help you. First your code:
int safe_read(int fd, char *buff, size_t len)
{
int pos= 0;
do{
errno=0;
pos+=read(fd, buff+pos, len);
if(-1==len){
if(ENTER!=errno){
return 0;
}
}
}while(ENTER==errno);
return pos;
}
Variable pos was not defined and even it it was defined global, you probably would have to initialize it to zero.
Then your funny variable ENTER, which is neither defined and, more importantly, is never set in your code. So it won't change value. What is your intention with this variable?
Then if(-1==len). len is a parameter that doesn't change so either it was -1 or it never will be. Clearly you want to check for an error on read, but this is not the way.
Then whether this is safe: no, it isn't. Assuming that len is the size of buff, then you repeatedly append len characters to buff, so at the second read it will go beyond the buffer's size.
And lastly whether this function will always return the correct number of characters read: no it doesn't. Suppose you read len characters the first time, and the second time it fails. You then return 0 but len characters had already been read.
I am reading from a file (each line wolds 1 word) and putting each line into an array. It crashes when its about to close the file saying (* glibc detected * proj: corrupted double-linked list: 0x0000000002139240 ***). Also everything but the 1st element was copied correctly (the 1st element was supposed to be "how are you" but was instead "0"). Any help on this is greatly appreciated.
int i = -1;
int numb;
int wsize;
while (fgets(word,30,file)!=NULL)
{
if (i==-1)
{
if(word[strlen(word)-1]=='\n')
{
word[strlen(word)-1] = 0;
}
numb = atoi(word);
ptr = malloc(sizeof(char*)*numb);
}
else
{
if(word[strlen(word)-1]=='\n')
{
word[strlen(word)-1] = 0;
}
wsize = strlen(word);
ptr[i] = malloc(sizeof(char*)*wsize);
strncpy(ptr[i],word,strlen(word));
size++;
}
i++;
}
int j=0;
while(j<16) //prints to see if they were copied corectly
{ //ptr[0] was the only one that did not copy corectly
printf("%s\n",ptr[j]);
j++;
}
fclose(file);
printf("test\n"); //was never printed so I assume it crashes at fclose()
return 1;
The line ptr[i] = malloc(sizeof(char*)*wsize); is wrong, for two reasons:
It should be sizeof(char), not sizeof(char*) (or just omit this, since sizeof(char) is equal to 1 by definition)
If you want to store a string of length wsize, you need to allocate wsize+1 bytes
EDIT — More issues:
What is the purpose of the line size++;? Did you mean wsize++;?
Where does the number 16 come from in while(j<16)? I suggest you try while(j<i) instead.
If main() returns a nonzero value, this signifies that an error occurred. Change this to return 0; unless you have a good reason for returning some other value.
One more:
I just noticed you're using strncpy(). This won't add a terminating '\0' byte to the end of the string because you've asked it to copy a string of length wsize using no more than wsize bytes. Change this line to strcpy(ptr[i],word); and it should work.
After you've done that, you need to remove all the potential buffer overflows from your code. (There are lots of them.)
I am trying to deconstruct a document into its respective paragraphs, and input each paragraphs, as a string, into an array. However, each time a new value is added, it overwrites all previous values in the array. The last "paragraph" read (as denoted by newline) is the value of each non-null value of the array.
Here is the code:
char buffer[MAX_SIZE];
char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));
int pp = 0;
int i;
FILE *doc;
doc = fopen(argv[1], "r+");
assert(doc);
while((i = fgets(buffer, sizeof(buffer), doc) != NULL)) {
if(strncmp(buffer, "\n", sizeof(buffer))) {
paragraphs[pp++] = (char*)buffer;
}
}
printf("pp: %d\n", pp);
for(i = 0; i < MAX_SIZE && paragraphs[i] != NULL; i++) {
printf("paragraphs[%d]: %s", i, paragraphs[i]);
}
The output I receive is:
pp: 4
paragraphs[0]: paragraph four
paragraphs[1]: paragraph four
paragraphs[2]: paragraph four
paragraphs[3]: paragraph four
when the program is run as follows: ./prog.out doc.txt, where doc.txt is:
paragraph one
paragraph two
paragraph three
paragraph four
The behavior of the program is otherwise desired. The paragraph count works properly, ignoring the line that contains ONLY the newline character (line 4).
I assume the problem occurs in the while loop, however am unsure how to remedy the problem.
Your solution is pretty sound. Your Paragraph array is supposed to hold each paragraph, and since each paragraph element is just a small 4 bytes pointer you can afford to define a reasonable max number of them. However, since this max number is a constant, it is of little use to allocate the array dynamically.
The only meaningful use of dynamic allocation would be to read the whole text once to count the actual number of paragraphs, allocate the array accordingly and re-read the whole file a second time, but I doubt this is worth the effort.
The downside of using fixed-size paragraph array is that you must stop filling it once you reach the maximal number of elements.
You can then re-allocate a bigger array if you absolutely want to be able to process the whole Bible, but for an educational exercise I think it's reasonable to just stop recording paragraphs (thus producing a code that can store and count paragraphs up to a maximal number).
The real trouble with your code is, you don't store the paragraph contents anywhere. When you read the actual lines, it's always inside the same buffer, so each paragraph will point to the same string, which will contain the last paragraph read.
The solution is to make a unique copy of the buffer and have the current paragraph point to that.
C being already messy enough as it is, I suggest using the strdup() function, which duplicates a string (basically computing string length, allocating sufficient memory, copying the string into it and returning the new block of memory holding the new copy). You just need to remember to free this new copy once you're done using it (in your case at the end of your program).
This is not the most time-efficient solution, since each string will require a strlen and a malloc performed internally by strdump while you could have pre-allocated a big buffer for all paragraphs, but it is certainly simpler and probably more memory-efficient (only the minimal amount of memory will be allocated for each string, though each malloc consumes a few extra bytes for internal allocator housekeeping).
The bloody awkward fgets also stores the trailing \n at the end of the line, so you'll probably want to get rid of that.
Your last display loop would be simpler, more robust and more efficient if you simply used pp as a limit, instead of checking uninitialized paragraphs.
Lastly, you'd better define two different constants for max line size and max number of paragraphs. Using the same value for both makes little sense, unless you're processing perfectly square texts :).
#define MAX_LINE_SIZE 82 // max nr of characters in a line (including trailing \n and \0)
#define MAX_PARAGRAPHS 100 // max number of paragraphs in a file
void main (void)
{
char buffer[MAX_LINE_SIZE];
char * paragraphs[MAX_PARAGRAPHS];
int pp = 0;
int i;
FILE *doc;
doc = fopen(argv[1], "r+");
assert(doc != NULL);
while((fgets(buffer, sizeof(buffer), doc) != NULL)) {
if (pp != MAX_PARAGRAPHS // make sure we don't overflow our paragraphs array
&& strcmp(buffer, "\n")) {
// fgets awkwardly collects the ending \n, so get rid of it
if (buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = '\0';
// current paragraph references a unique copy of the actual text
paragraphs[pp++] = strdup (buffer);
}
}
printf("pp: %d\n", pp);
for(i = 0; i != pp; i++) {
printf("paragraphs[%d]: %s", i, paragraphs[i]);
free(paragraphs[i]); // release memory allocated by strdup
}
}
What is the proper way to allocate the necessary memory? Is the malloc on line 2 not enough?
No, you need to allocate memory for the 2D array of strings you created. The following will not work as coded.
char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));
If you have: (for a simple explanation)
char **array = {0}; //array of C strings, before memory is allocation
Then you can create memory for it like this:
int main(void)
{
int numStrings = 10;// for example, change as necessary
int maxLen = MAX_SIZE; //for example, change as necessary
char **array {0};
array = allocMemory(array, numStrings, maxLen);
//use the array, then free it
freeMemory(array, numStrings);
return 0;
}
char ** allocMemory(char ** a, int numStrings, int maxStrLen)
{
int i;
a = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(sizeof(char)*maxStrLen + 1, sizeof(char));
}
return a;
}
void freeMemory(char ** a, int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(a[i]) free(a[i]);
free(a);
}
Note: you can determine the number of lines in a file several ways, One way for example, by FILE *fp = fopen(filepath, "r");, then calling ret = fgets(lineBuf, lineLen, fp) in a loop until ret == EOF, keeping count of an index value for each loop. Then fclose(). (which you did not do either) This necessary step is not included in the code example above, but you can add it if that is the approach you want to use.
Once you have memory allocated, Change the following in your code:
paragraphs[pp++] = (char*)buffer;
To:
strcpy(paragraphs[pp++], buffer);//no need to cast buffer, it is already char *
Also, do not forget to call fclose() when you are finished with the open file.
I'm stuck with an apparently harmless piece of code. I'm trying to read a whole flv video file into a uint8_t array, but by no reason only the 10 first bytes are read.
contents = malloc(size + 1);
if (read(fd, contents, size) < 0)
{
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
I've tried with fopen and "rb" also, but seems that Glibc ignores that extra 'b' or something. Any clues?
Thanks in advance.
Edit: Maybe it reads a EOF character?
PS. 'size' is a variable containing the actual file size using stat().
It seems the original code correctly reads the entire content.
The problem seems to be in making use of that binary data - printing it out will truncate at the first null, making it appear that only 10 bytes are present.
You can't use any methods intended for strings or character arrays to output binary data, as they will truncate at the first null byte, making it appear the array is shorter than it really is.
Check out some other questions related to viewing hex data:
how do I print an unsigned char as hex in c++ using ostream?
Converting binary data to printable hex
If you want to append this to a string - in what format? hex? base64? Raw bytes won't work.
Here's the original code I posted. A few minor improvements, plus some better diagnostic code:
int ret, size = 4096; /* Probably needs to be much bigger */
uint8_t *contents;
contents = malloc(size + 1);
if(contents == NULL)
{
log_message(WARNING, __func__, EMSG_MEMORY);
return (NULL);
}
ret = read(fd, contents, size);
if(ret < 0)
{
/* Error reading file */
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
for(i = 0;i < ret;++i)
{
printf("%c", contents[i]);
/* printf("%0.2X", (char) contents[i]); /* Alternatively, print in hex */
}
Now, is ret really 10? Or do you just get 10 bytes when you try to print the output?
The 'read()' function in the C library doesn't necessarily return the whole read in one shot. In fact, if you're reading very much data at all, it usually doesn't give it to you in a single call.
The solution to this is to call read() in a loop, continuing to ask for more data until you've got it all, or until read returns an error, indicated by a negative return value, or end-of-file, indicated by a zero return value.
Something like the following (untested):
contents = malloc(size + 1);
bytesread = 0;
pos = 0;
while (pos < size && (bytesread = read(fd, contents + pos, size - pos)) > 0)
{
pos += bytesread;
}
if (bytesread < 0)
{
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
/* Go on to use 'contents' now, since it's been filled. Should probably
check that pos == size to make sure the file was the size you expected. */
Note that most C programmers would do this a little differently, probably making 'pos' a pointer which gets moved along, rather than offsetting from 'contents' each time through the loop. But I thought this approach might be clearer.
On success, read() returns the number of bytes read (which may be less than what you asked for, at which point you should ask for the rest.) On EOF it will return 0 and on error it will return -1. There are some errors for which you might want to consider re-issuing the read (eg. EINTR which happens when you get a signal during a read.)