niallo27
niallo27

Reputation: 215

Program that reads in a text file as input and produces an output file which has all the original lines in alphabetical order

The code below works perfectly for what we need, but we were told late in the week that we cannot use strcmp as we have not covered it yet. Have you guys any suggestion of what i could use instead. I got help with the original code as i am new to pointers. Thanks

Ok this is my new code. It is not sorting the text though.

I'm new so go easy

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Function for reading unsorted data from a file
int read(const char *filename, char* arr[]);

// Function for sorting gathered data in array
void sort(char* arr[], int N);

int my_strcmp(const char *a, const char *b);

// Function for writing sorted data to file
int write(const char *filename, char* arr[], int N);

// Helper function for swapping two words
void swap(char** a, char** b);

// Helper function for dealocatiing memory used by array
void final(char* arr[], int N);

int main(int argc, char *argv[])
{
    char filename[260];
    char* arr[1000];            // Suppose there are no more than 1000 lines
    int N;                      // Size of filled array
// Request for input file name
    printf("Input file name :  ");
scanf("%s",filename);
// Perform reading file
if (-1 == (N = read(filename,arr))) {
    // If error occure while opening the file
    printf("File can't be opened.\n");
    system("PAUSE");
    return 0;
}
// Sort the data
   sort(arr,N);
   printf("Data sorted successfully.\n");
// Request for output file name
   printf("Output file name : ");
   scanf("%s",filename);
// Output sorted data
   if (-1 == (N = write(filename,arr,N))) {
    // If error occure while opening the file
    printf("File can't be opened.\n");
    system("PAUSE");
    return 0;
}
system("PAUSE");
// Deallocate used memory
final(arr,N);
return 0;
}

int read(const char *filename, char* arr[])
{
char buffer[512];       // Suppose that line can't be longer than 511 symbols
int i = 0, len;
FILE * infile;          // Input file
// Open file for reading
infile = fopen(filename,"r");
// Check whether file was opened correctly
if (!infile)
    // If not then return error code
    return -1;
// Read data
while (!feof(infile)) {
       // Read one line (word)
    fscanf(infile,"%s",buffer);
       // Find the length of this word
    len = strlen(buffer);
       // Allocate memory for the word...
    arr[i] = (char*)malloc(len+1);
       // And copy the word to array
    strcpy(arr[i],buffer);
       // Increase counter
    ++i;
}
// Close the file
fclose(infile);
// Return number of elements of created array
return i;
}

void sort(char* arr[], int N)
{
int i,cmp;
for (--N; N>0; --N)
    for (i=0; i<N; ++i) {
        // Compare two words
        cmp = my_strcmp(arr[i],arr[i+1]);
        // If the first one is greater than second one (in alphabetical meaning)...
        if (cmp>0)
            // ...then swap them
            swap(&arr[i],&arr[i+1]);
    }
}

int write(const char *filename, char* arr[], int N)
{
int i;
FILE * outfile; // Output file
// Open file for writing
outfile = fopen(filename,"w");
// Check whether file was opened correctly
if (!outfile)
        // If not then return error code
    return -1;
// Write data
for (i=0; i<N-1; ++i)
    // After each word output sign '\n' which means "new line"
    fprintf(outfile,"%s\n",arr[i]);
// But after the last word don't write "new line"
fprintf(outfile,"%s",arr[N-1]);
// Close the file
fclose(outfile);
// Return success code
return 0;
}

void swap(char** a, char** b)
{
char *temp = *a;
*a = *b;
*b = temp;
}

void final(char* arr[], int N)
{
int i;
for (i=0; i<N; ++i)
    free(arr[i]);
}

int my_strcmp(const char *a, const char *b)
{
while(*a==*b)
{
    if ( *a == '\0' || *b == '\0' )
     break;

  a++;
  b++;
}
   if( *a == '\0' && *b == '\0' )
  return 0;
else
   return -1;
  }

Upvotes: 0

Views: 1500

Answers (1)

sonologico
sonologico

Reputation: 764

As @Gopi and @iharob suggested, you should write a strcmp replacement. First, remember to add a declaration before main with the same signature as strcmp:

int my_strcmp(const char *a,const char *b);

Also remember to change the cmp = strcmp(arr[i],arr[i+1]); call in the sort function to the new name cmp = my_strcmp(arr[i], arr[i+1]);.

Then write your own strcmp version. It's a simple function and you have already described what it does. cppreference gives a complete description of the function:

Defined in header

int strcmp( const char *lhs, constchar *rhs );

Compares two null-terminated byte strings lexicographically.

The sign of the result is the sign of the difference between the values of the first pair of characters (both interpreted as unsigned char) that differ in the strings being compared.

The behavior is undefined if lhs or rhs are not pointers to null-terminated strings.

Parameters

lhs, rhs - pointers to the null-terminated byte strings to compare.

Return value

Negative value if lhs appears before rhs in lexicographical order.

Zero if lhs and rhs compare equal.

Positive value if lhs appears after rhs in lexicographical order.

You a have already described a simple implementation: "compare the first character of each string and if they are equal, compare the next two". If they aren't equal, you return a negative value if the first comes before the second in a lexicographical order and a positive value if it is the opposite. You return 0 if the strings are all equal.

int my_strcmp(const char *a, const char *b)
{
    /* Your code here */
}

Things to have in mind:

  • Strings in c always end with the '\0' character, so you use that to test the end of a string. You can iterate over the whole string either incrementing the pointer or indexing it like an array and incrementing the index.
  • In lexicographical ordering, if you have strings of different lengths, but that are equal up to the point that the shorter one ends, the shorter one comes before the longer one. Example: "fly" comes before "flying".
  • The cppreference link describes a different implementation that will use less comparisons than yours and will probably be more efficient, but yours will behave correctly too.

Edit: Discussing your newly added function implementation. I won't provide an optimal or beautiful implementation, I will just discuss your code with the intention of helping you to achieve something that works correctly starting from the code you posted.

Your code (with better indentation):

int my_strcmp(const char *a, const char *b)
{
    while(*a==*b)
    {
        if ( *a == '\0' || *b == '\0' )
            break;

            a++;
            b++;
    }
    if( *a == '\0' && *b == '\0' )
        return 0;
    else
        return -1;
  }

The while loop iterates over both strings while they're equal. You also break the loop if you have reached the end of one of the strings by testing for the '\0' character. That's all very good. Your problem is after the loop.

There are four possible states after the loop:

  • You have reached the end of both strings. Both *a and *b are '\0'. That means that all characters were equal and the strings are, therefore, equal.
  • *a is '\0', but *b is a valid character. That means that the first string is shorter than the second, but they were equal up to the point that the first ended. That means that the first comes before the second.
  • The opposite of the previous state. *b is '\0' and *a is a valid character. That means the second comes before the first.
  • Both *a and *b are valid characters, they are just different. Testing the characters will define which string comes first.

Remember that you should return a negative value if the first string comes before the second, 0 if they are equal or a positive value the second string comes before the first. You could just test all possible states naively and return the appropriate value. After you have a working implementation, you can think about how you could keep the same behaviour while "cutting corners" to make it more succinct if needed.

Upvotes: 1

Related Questions