So I have an assignment where I'm supposed to use read and write to read in lines from a file from stdin, sort it, then write it out to stdout with write. However, I can't get it to work.
I have to call a sort function on buf first to sort the string (which I got using read) before I re-output it. Can I treat buf as an array? Or does it not work like that? The reason is because I have to sort the string first.
int record_compare(const void *a, const void *b)
{
return (memcmp(a, b, num_bytes));
}
qsort(buf, num_elements, num_bytes, record_compare);
while (count < n - num_bytes)
{
i = memcmp(buf+count, buf+count + num_bytes, num_bytes);
if (i == 0)
count = count + num_bytes;
else
{
for (k = 0; k < num_bytes; k++)
{
printf("%c", buf[count]);
count++;
}
}
}
But since I got the string with read instead of something like fgets, can I still treat buf as an array? This is how the sort works on a normal array (it sorts it then prints out without repeats.
do {
c = read(0, buf+n, 1);
if (c != 0)
n++;
}
while (c != 0);
This is how I got buf.
The read() system call will read N bytes (1 byte at a time the way you've written the code). You will want to read up until you get a newline, or until you get zero bytes returned (which marks EOF), or until you get an error. Assuming you found a newline, you will start a new buffer.
You'll probably have an array of character pointers, and you'll probably allocate a new string for each line, and store the pointer in the array of character pointers.
enum { MAX_LINES = 1024 };
char *lines[MAX_LINES];
int n_lines = 0;
I'm assuming a fixed upper bound is OK; you could arrange to dynamically allocate the array of pointers too.
So, you'll need a function to read a line and store it.
Then you have a problem with your compare function. You'll be calling:
qsort(lines, n_lines, sizeof(char *), record_compare);
But record_compare() will be given two char ** values, so you need to use:
int record_compare(const void *v1, const void *v2)
{
const char *s1 = *(char **)v1;
const char *s2 = *(char **)v2;
return(strcmp(s1, s2));
}
Writing the sorted data is pretty straight forward. So, you need to concentrate on the line reader function.
Related
I have the following function to print out text to an LCD. Everything works, although I'm having some trouble understanding how it works and why it's written this way.
I understand it takes a string array, and loops through it to write each character to the LCD.
What I don't understand is why does it use a buffer instead of just accessing the str variable directly.
And why does it return the length of the string that was written out.
Also why does my write function also need to return size_t.
// This will print character string to the LCD
size_t print(const char str[]) {
if (str == NULL) return 0;
const uint8_t *buffer = (const uint8_t *)str;
size_t size = strlen(str);
size_t n = 0;
while (size--) {
if (write(*buffer++)) n++;
else break;
}
return n;
why does it use a buffer instead of just accessing the str variable directly?
Because the write() function takes a uint8_t parameter, but str contains char. So this avoids having to write a cast in the write() call. It would work just as well using
if (write((uint8_t)*(str++))) n++;
but this is harder to read.
why does it return the length of the string that was written out?
So the caller will know how many characters were successfully written, in case write() reports an error in the middle of the string (by returning 0).
why does my write function also need to return size_t?
I can't think of a reason, other than for consistency with functions. write() should just return either 1 when it writes successfully or 0 when it fails.
Please checkout the code changes below with the comments
size_t print(const char *str) { // As it always degrades into a pointer!
if (str == NULL) {
return 0;
} // Always use braces - do not wish to get you pants down!
// Is this needed? const uint8_t *buffer = (const uint8_t *)str;
// Do not need this - counting a string twice ! size_t size = strlen(str);
// Not requires dsize_t n = 0;
//while (size--) {
for (size_t n = 0; str[n]; n++) {
write(buffer[n]); //if (write(*buffer++)) n++;
// else break; for loop removes the requirement
}
return n;
}
Please note write is a part of unidstd see unistd - So some confusion here
I need to write c program to compare two strings without using strncpy() and then in another funtion check whether it works with using assert()
In my code by checking with pointers assert(*str2 == *"comp"); I only check the first letter and nothing else.So even there is another word starting with c, it will work.
char initialize_n(char str1[], char str2_n[], int n)
{
//initialize a string from the (at most) n first characters of another string
int i = 0;
for(i = 0; i < n; i++)
{
str2_n[i] = str1[i];
}
str2_n[i] = '\0';
}
void test_initialize_n(){
char str1[100] = "computer";
char str2[100];
initialize_n(str1, str2, 4);
assert(*str2 == *"comp");
}
How to correctly check it with assert?
So, without gcc extensions you can't put this into the assert body because the functions that would do so are forbidden to you.
What you need to do is write your own compare function and call it. Skeleton as follows:
int prefix_equals(const char *left, const char *right, int nleft, int nright)
{
if (nleft != nright) return 0;
/* Use the same for loop here you have in initialize_n */
if (left[i] != right[i]) return 0;
return 1; /* If you got all the way through they're equal */
}
assert(prefix_equals(str2, "comp", 4, 4));
In my experience the most useful form of this actually has two length arguments to save the caller from some ugliness in the call point. It seems wrong at assert level but it isn't wrong when you get to a few thousand lines.
The purpose of this function is to copy a string into a "buffer" - essentially another string. However, the problem seems overly complicated than what would be practical.
"Copies at most n-1 characters of string in into the buffer pointed to by
out. If n is reached, returns -2. Otherwise, returns -1 for malformed
input and 0 upon successful completion."
This is what I have:
#include <stdio.h>
#include <assert.h>
int copyStringN(register char *in, register char *out, register int n){
//Declarations
int i; //Dummy index
if(!in || !out) return -1;
for(i=0; i<n; i++){
*out++ = *in++;
}
*out = '\0';
return 0;
}
int main(void){
//Declarations
char in[] = "I'm not trying to inject malicious code...\\x29A.";
const int N = sizeof(in);
char out[N];
int err;
//Main execution
printf("\nThis function will copy at most n-1 characters of string into\nthe buffer pointed to by out.\n\n");
err = copyStringN(in, out, N);
assert(!err);
printf("%s\n", out);
printf("\nPlease press enter to exit...");
getchar();
return 0;
}
This general form was suggested, but it seems overly convoluted than what needs to be done. Why would n ever be reached? The execution should stop before n. Furthermore, wouldn't N = sizeof(in) match the length of the original string?
Personally, I would rather use a function closer to
int copyStringN(register char *in, register char *out)
{
if((!in || !out) && (sizeof(in)<=sizeof(out))) return -1;
else{
while(*t++ = *from++);
return 0;
}
}
int main(void){
//Declarations
char in[] = "I'm not trying to inject malicious code...\\x29A.";
const int N = sizeof(in);
char out[N];
int err;
.
.
.
I believe it would have the same effect with less statements. Let me make this more of a question, how could I write a function that copies a string into another array with the protection defined in the prompt? Also, are the two programs that I presented somehow vulnerable in a way I don't recognize?
Constructive input is appreciated.
This is a strange thing in C.
char mole[] = "mole" is not the same as char *mole = "mole"
I just tried:
char *a1 = "mole";
char a2[] = "mole";
printf ("s1: %i s2:%i\n", sizeof(a1), sizeof(a2) );
a1 is a pointer, so 4 or 8 depending on the architecture.
a2 is an array of size 5.
But you can convert a2 to a char* without warnings. But you loose the size.
Your suggested alternative will not work. (sizeof(in)<=sizeof(out) will always be TRUE, because you are comparing pointers (and not arrays), and they are the same size.
If you want to make safe string copy function, you must always pass output buffer length for size checking, and have means to inform user if input was too long for output.
Edit:
Since people have suggested to use strncpy, I will present safer alternative:
int len = snprintf(output, OUTPUT_SIZE, "%s", input);
if(len < 0 || len >= OUTPUT_SIZE) {
// Failed, handle error
}
Fewer statements in your source does not necessarily imply that it is simpler to grasp. The while row in your alternative solution may work, but is doing too many things at the same time for my taste. You are writing code first for other human beings to read, then for a compiler to read.
I like for example making the NULL and \0 checking explicit.
It is also unclear what you are trying to achieve with the sizeof comparison. Besides comparing size of pointers (instead of intended arrays?), I think you meant || instead of &&. If either pointer is NULL it is an error, whatever the size.
int copyStringN(char *in, char *out)
{
if((in == NULL) || (out == NULL)) {
return -1;
} else {
while(*in != '\0') {
*out++ = *in++;
}
*out = '\0';
return 0;
}
}
The compiled code is probably not going to be much different, only the source is more human readable in my opinion.
Then if there happens to be no '\0' in the in string you are going to have problems. I suppose this is the reason for having a length limit n.
while((*in != '\0') && (n-- > 0)) {
*out++ = *in++;
}
*out = '\0';
Note that you would still be in trouble if n is greater than the size of your arrays and you miss a '\0'.
A very similar interface is strncpy. Perhaps the error modes will make more sense after you read the man page.
It is always better to use strncpy to prevent buffer overflow. char * strncpy ( char * destination, const char * source, size_t num ); Also, better to use strlen rather than sizeof. So, Even if the source string is greater than the destination buffer. It will protect the destination buffer from buffer overflow. I would use n as the maximum size of the destination buffer. And In fact make n = strlen(dest_buffer) -1. To accommodate '\0'.
a basic strncpy would look like:
char *strncpy(char *d,const char *s,int n){int i=0;while(n--&&d[i++]=*s++);return d;}
but you could force a null byte at n-1
char *sstrncpy(char *d, const char *s, int n){
int i=0;
while(--n&&d[i++]=*s++);
if(!n)d[i]=0;
return d;
}
I wanna get string like 34,34;34,21;45,12;45,12(length is not certain.)
I wanna dynamic memory allocation with realloc but i can't do it.
How i can get like this characters to string??
it will be string={34,34,34,21,45,12,45,12}
You will have to know the length beforehand, and when you know that your buffer is too small for data that is going to be newly entered, use:
realloc(ptr, newLength);
If you're looking to do this at compile time (which is the only way to perform initializers similar to what you have in your question), you can let the initializer define the size f your array:
char string[] = {34,34,34,21,45,12,45,12, 0}; // added a 0 to the end to
// make it '\0' terminated
// You might not need that
If you want your string to take it's data from a runtime source (a file or other input), you'll need to perform the allocation yourself, and exactly how to do it depends on how you're going to be getting the data.
The following example reads data from stdin into a dynamically allocated character array, growing the array as needed until EOF is reached. It grows the array by 20 bytes each time so you can easily check what's happening in a debugger, but a real life program would do better to grow by something larger like by doubling the size or just growing in increments of 100KB - the details of your expected data should guide you in this decision).
#include <stdlib.h>
#include <stdio.h>
void fatal_error(void);
int main( int argc, char** argv)
{
int buf_size = 0;
int buf_used = 0;
char* buf = NULL;
char* tmp = NULL;
char c;
int i = 0;
while ((c = getchar()) != EOF) {
if (buf_used == buf_size) {
//need more space in the array
buf_size += 20;
tmp = realloc(buf, buf_size); // get a new larger array
if (!tmp) fatal_error();
buf = tmp;
}
buf[buf_used] = c; // pointer can be indexed like an array
++buf_used;
}
puts("\n\n*** Dump of stdin ***\n");
for (i = 0; i < buf_used; ++i) {
putchar(buf[i]);
}
free(buf);
return 0;
}
void fatal_error(void)
{
fputs("fatal error - out of memory\n", stderr);
exit(1);
}
Maybe you are passing the pointer as an argument to a function b() which is in turn calling the realloc
In this case you need to also return the pointer.
I'm reading in a line from a file (char by char, using fgetc()), where all fields(firstname, lastname, ...) are seperated by an ;. What I now want to do is create a char**, add all the chars to that and replace the ; by \0 so that I effectively get a list of all the fields.
Is that actually possibly? And when I create a char**, e.g. char ** buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?
EDIT
Sry, meant to replace ; by \0, bot \n.
EDIT 2
This code should demonstrate what I intend to do (may clarify things a little):
int length = 80; // initial length char *buf = malloc(length); int num_chars = 0;
// handle malloc returning NULL
// suppose we already have a FILE pointer fp for (int ch = fgetc(fp); ch != EOF && ferror(fp) == 0; ch = fgetc(fp)) {
if (length == size) { // expand array if necessary
length += 80;
buf = realloc(buf, length);
// handle realloc failure
}
if (ch == ';') {
buf[num_chars] = '\0'; // replace delimiter by null-terminator
} else {
buf[num_chars] = ch; // else put char in buf
} }
// resize the buffer to chars read buf
= realloc(buf, num_chars);
// now comes the part where I'm quite unsure if it works / is possible
char **list = (char **)buf; // and now I should be able to handle it like a list of strings?
Yes, it's possible. Yes, you can treat it as a one dimensional array. Yes, the memory is contiguous.
But you probably want:
char ** fields = malloc(sizeof(char *) * numFields);
Then you can do:
// assuming `field` is a `char *`
fields[i++] = field;
It's not exactly possible as you describe it, but something similar is possible.
More specifically, the last line of your example char **list = (char **)buf; won't do what you believe. char **list means items pointed by *list will be of the type char*, but the content of *buf are chars. Hence it won't be possible to change one into another.
You should understand that a char ** is just a pointer, it can hold nothing by itself. But the char ** can be the start address of an array of char *, each char * pointing to a field.
You will have to allocate space for the char * array using a first malloc (or you also can use a static array of char * instead of a char**. You also will have to find space for every individual field, probably performing a malloc for each field and copying it from the initial buffer. If the initial buffer is untouched after reading, it would also be possible to use it to keep the field names, but if you just replace the initial ; by a \n, you'll be missing the trailing 0 string terminator, you can't replace inplace one char by two char.
Below is a simplified example of what can be done (removed malloc part as it does not add much to the example, and makes it uselessly complex, that's another story):
#include <stdio.h>
#include <string.h>
main(){
// let's define a buffer with space enough for 100 chars
// no need to perform dynamic allocation for a small example like this
char buf[100];
const char * data = "one;two;three;four;";
// now we want an array of pointer to fields,
// here again we use a static buffer for simplicity,
// instead of a char** with a malloc
char * fields[10];
int nbfields = 0;
int i = 0;
char * start_of_field;
// let's initialize buf with some data,
// a semi-colon terminated list of strings
strcpy(buf, data);
start_of_field = buf;
// seek end of each field and
// copy addresses of field to fields (array of char*)
for (i = 0; buf[i] != 0; i++){
if (buf[i] == ';'){
buf[i] = 0;
fields[nbfields] = start_of_field;
nbfields++;
start_of_field = buf+i+1;
}
}
// Now I can address fields individually
printf("total number of fields = %d\n"
"third field is = \"%s\"\n",
nbfields, fields[2]);
}
I now want to do is create a char**, add all the chars to that
You would allocate an array of char (char*) to store the characters, not an array of char* (char**).
And when I create a char*, e.g. char * buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?
Yes, malloc always returns continguous blocks. Yes, you can treat it as single-dimensional an array of char* (with 80/sizeof char* elements). But you'll need to seperately allocate memory for each string you store in this array, or create another block for storing the string data, then use your first array to store pointers into that block.
(or something else; lots of ways to skin this cat)
It may be that Helper Method wants each of the "fields" written to a nul-terminated string. I cannot tell. You can do that with strtok(). However strtok() has problems - it destroys the original string "fed" to it.
#define SPLIT_ARRSZ 20 /* max possible number of fields */
char **
split( char *result[], char *w, const char *delim)
{
int i=0;
char *p=NULL;
for(i=0, result[0]=NULL, p=strtok(w, delim); p!=NULL; p=strtok(NULL, delim), i++ )
{
result[i]=p;
result[i+1]=NULL;
}
return result;
}
void
usage_for_split(void)
{
char *result[SPLIT_ARRSZ]={NULL};
char str[]="1;2;3;4;5;6;7;8;9;This is the last field\n";
char *w=strdup(str);
int i=0;
split(result, w, ";\n");
for(i=0; result[i]!=NULL; i++)
printf("Field #%d = '%s'\n", i, result[i]);
free(w);
}