Changing the extension of a passed filename - c

My function is passed a filename of the type
char *myFilename;
I want to change the existing extension to ".sav", or if there is no extension, simply add ".sav" to the end of the file. But I need to consider files named such as "myfile.ver1.dat".
Can anyone give me an idea on the best way to achieve this.
I was considering using a function to find the last "." and remove all characters after it and replace them with "sav". or if no "." is found, simple add ".sav" to the end of the string. But not sure how to do it as I get confused by the '\0' part of the string and whether strlen returns the whole string with '\0' or do I need to +1 to the string length after.
I want to eventual end up with a filename to pass to fopen().

May be something like this :
char *ptrFile = strrchr(myFilename, '/');
ptrFile = (ptrFile) ? myFilename : ptrFile+1;
char *ptrExt = strrchr(ptrFile, '.');
if (ptrExt != NULL)
strcpy(ptrExt, ".sav");
else
strcat(ptrFile, ".sav");
And then the traditional way , remove and rename

Here's something lazy I've whipped up, it makes minimum use of the standard library functions (maybe you'd like something that does?):
#include <stdio.h>
#include <string.h>
void change_type(char* input, char* new_extension, int size)
{
char* output = input; // save pointer to input in case we need to append a dot and add at the end of input
while(*(++input) != '\0') // move pointer to final position
;
while(*(--input) != '.' && --size > 0) // start going backwards until we encounter a dot or we go back to the start
;
// if we've encountered a dot, let's replace the extension, otherwise let's append it to the original string
size == 0 ? strncat(output, new_extension, 4 ) : strncpy(input, new_extension, 4);
}
int main()
{
char input[10] = "file";
change_type(input, ".bff", sizeof(input));
printf("%s\n", input);
return 0;
}
And it indeed prints file.bff. Please note that this handles extensions up to 3 chars long.

strlen returns the number of characters in the string but arrays are indexed from 0 so
filename [strlen(filename)]
is the terminating null.
int p;
for (p = strlen (filename) - 1; (p > 0) && (filename[p] != '.'); p--)
will loop to zero if no extension and stop at the correct spot otherwise.

Related

Appending Characters to an Empty String in C

I'm relatively new to C, so any help understanding what's going on would be awesome!!!
I have a struct called Token that is as follows:
//Token struct
struct Token {
char type[16];
char value[1024];
};
I am trying to read from a file and append characters read from the file into Token.value like so:
struct Token newToken;
char ch;
ch = fgetc(file);
strncat(newToken.value, &ch, 1);
THIS WORKS!
My problem is that Token.value begins with several values I don't understand, preceding the characters that I appended. When I print the result of newToken.value to the console, I get #�����TheCharactersIWantedToAppend. I could probably figure out a band-aid solution to retroactively remove or work around these characters, but I'd rather not if I don't have to.
In analyzing the � characters, I see them as (in order from index 1-5): \330, \377, \377, \377, \177. I read that \377 is a special character for EOF in C, but also 255 in decimal? Do these values make up a memory address? Am I adding the address to newToken.value by using &ch in strncat? If so, how can I keep them from getting into newToken.value?
Note: I get a segmentation fault if I use strncat(newToken.value, ch, 1) instead of strncat(newToken.value, &ch, 1) (ch vs. &ch).
I'll try to consolidate the answers already given in the comments.
This version of the code uses strncat(), as yours, but solving the problems noted by Nick (we must initialize the target) and Dúthomhas (the second parameter to strncat() must be a string, and not a pointer to a single char) (Yes, a "string" is actually a char[] and the value passed to the function is a char*; but it must point to an array of at least two chars, the last one containing a '\0'.)
Please be aware that strncat(), strncpy() and all related functions are tricky. They don't write more than N chars. But strncpy() only adds the final '\0' to the target string when the source has less than N chars; and strncat() always adds it, even if it the source has exactly N chars or more (edited; thanks, #Clifford).
#include <stdio.h>
#include <string.h>
int main() {
FILE* file = stdin; // fopen("test.txt", "r");
if (file) {
struct Token {
char type[16];
char value[1024];
};
struct Token newToken;
newToken.value[0] = '\0'; // A '\0' at the first position means "empty"
int aux;
char source[2] = ""; // A literal "" has a single char with value '\0', but this syntax fills the entire array with '\0's
while ((aux = fgetc(file)) != EOF) {
source[0] = (char)aux;
strncat(newToken.value, source, 1); // This appends AT MOST 1 CHAR (and always adds a final '\0')
}
strncat(newToken.value, "", 1); // As the source string is empty, it just adds a final '\0' (superfluous in this case)
printf(newToken.value);
}
return 0;
}
This other version uses an index variable and writes each singe char directly into the "current" position of the target string, without using strncat(). I think is simpler and more secure, because it doesn't mix the confusing semantics of single chars and strings.
#include <stdio.h>
#include <string.h>
int main() {
FILE* file = stdin; // fopen("test.txt", "r");
if (file) {
struct Token {
int index = 0;
char type[16];
char value[1024]; // Max size is 1023 chars + '\0'
};
struct Token newToken;
newToken.value[0] = '\0'; // A '\0' at the first position means "empty". This is not really necessary anymore
int aux;
while ((aux = fgetc(file)) != EOF)
// Index will stop BEFORE 1024-1 (value[1022] will be the last "real" char, leaving space for a final '\0')
if (newToken.index < sizeof newToken.value -1)
newToken.value[newToken.index++] = (char)aux;
newToken.value[newToken.index++] = '\0';
printf(newToken.value);
}
return 0;
}
Edited: fgetc() returns an int and we should check for EOF before casting it to a char (thanks, #chqrlie).
You are appending string that is not initialised, so can contain anything. The end I'd a string is indicated by a NUL(0) character, and in your example there happened to be one after 6 bytes, but there need not be any within the value array, so the code is seriously flawed, and will result in non-deterministic behaviour.
You need to initialise the newToken instance to empty string. For example:
struct Token newToken = { "", "" } ;
or to zero initialise the whole structure:
struct Token newToken = { 0 } ;
The point is that C does not initialise non-static objects without an explicit initialiser.
Furthermore using strncat() is very inefficient and has non-deterministic execution time that depends on the length of the destination string (see https://www.joelonsoftware.com/2001/12/11/back-to-basics/).
In this case you would do better to maintain a count of the number of characters added, and write the character and terminator directly to the array. For example:
size_t index ;
int ch = 0 ;
do
{
ch = fgetc(file);
if( ch != EOF )
{
newToken.value[index] = (char)ch ;
index++ ;
newToken.value[index] = '\0' ;
}
} while( ch != EOF &&
index < size of(newToken.value) - 1 ) ;

Is there a quick way to get the last element that was put in an array?

I use an fgets to read from stdin a line and save it in a char array, I would like to get the last letter of the line i wrote , which should be in the array before \nand \0.
For example if i have a char line[10] and write on the terminal 1stLine, is there a fast way to get the letter e rather than just cycling to it?
I saw this post How do I print the last element of an array in c but I think it doesn't work for me, even if I just create the array without filling it with fgets , sizeof line is already 10 because the array already has something in it
I know it's not java and I can't just .giveMeLastItem(), but I wonder if there is a smarter way than to cycle until the char before the \n to get the last letter I wrote
code is something like
char command[6];
fgets(command,6,stdin);
If you know the sentinel value, ex: \0 (or \n ,or any value for that matter), and you want the value of the element immediately preceding to that, you can
use strchr() to find out the position of the sentinel and
get the address of retPtr-1 and dereference to get the value you want.
There are many different ways to inspect the line read by fgets():
first you should check the return value of fgets(): a return value of NULL means either the end of file was reached or some sort of error occurred and the contents of the target array is undefined. It is also advisable to use a longer array.
char command[80];
if (fgets(command, sizeof command, stdin) == NULL) {
// end of file or read error
return -1;
}
you can count the number of characters with len = strlen(command) and if this length os not zero(*), command[len - 1] is the last character read from the file, which should be a '\n' if the line has less than 5 bytes. Stripping the newline requires a test:
size_t len = strlen(command);
if (len > 0 && command[len - 1] == '\n')
command[--len] = '\0';
you can use strchr() to locate the newline, if present with char *p strchr(command, '\n'); If a newline is present, you can strip it this way:
char *p = strchar(command, '\n');
if (p != NULL)
*p = '\0';
you can also count the number of characters no in the set "\n" with pos = strcspn(command, "\n"). pos will point to the newline or to the null terminator. Hence you can strip the trailing newline with:
command[strcspn(command, "\n")] = '\0'; // strip the newline if any
you can also write a simple loop:
char *p = command;
while (*p && *p != '\n')
p++;
*p = '\n'; // strip the newline if any
(*) strlen(command) can return 0 if the file contains an embedded null character at the beginning of a line. The null byte is treated like an ordinary character by fgets(), which continues reading bytes into the array until either size - 1 bytes have been read or a newline has been read.
Once you have only the array, there is no other way to do this. You could use strlen(line) and then get the last characters position based on this index, but this basically does exactly the same (loop over the array).
char lastChar = line[strlen(line)-1];
This has time-complexity of O(n), where n is the input length.
You can change the input method to a char by char input and count the length or store the last input. Every O(1) method like this uses O(n) time before (like n times O(1) for every character you read). But unless you have to really speed optimize (and you don't, when you work with user input) should just loop over the array by using a function like strlen(line) (and store the result, when you use it multiple times).
EDIT:
The strchr() function Sourav Ghosh mentioned, does exactly the same, but you can/must specify the termination character.
A straightforward approach can look the following way
char last_letter = command[ strcspn( command, "\n" ) - 1 ];
provided that the string is not empty or contains just the new line character '\n'.
Here is a demonstrative progarm.
#include <stdio.h>
#include <string.h>
int main(void)
{
enum { N = 10 };
char command[N];
while ( fgets( command, N, stdin ) && command[0] != '\n' )
{
char last_letter = command[ strcspn( command, "\n" ) - 1 ];
printf( "%c ", last_letter );
}
putchar( '\n' );
return 0;
}
If to enter the following sequence of strings
Is
there
a
quick
way
to
get
the
last
element
that
was
put
in
an
array?
then the output will be
s e a k y o t e t t t s t n n ?
The fastest way is to keep an array of references like this:
long ref[]
and ref[x] to contain the file offset of the last character of the xth line. Having this reference saved at the beginning of the file you will do something like:
fseek(n*sizeof(long))
long ref = read_long()
fseek(ref)
read_char()
I think this is the fastest way to read the last character at the end of the nth line.
I did a quick test of the three mentioned methods of reading a line from a stream and measuring its length. I read /usr/share/dict/words 100 times and measured with clock()/1000:
fgets + strlen = 420
getc = 510
fscanf with " 100[^\n]%n" = 940
This makes sense as fgets and strlen just do 2 calls, getc does a call per character, and fscanf may do one call but has a lot of machinery to set up for processing complex formats, so a lot more overhead. Note the added space in the fscanf format to skip the newline left from the previous line.
Beside the other good examples.
Another way is using fscanf()/scanf() and the %n format specifier to write to an argument the amount of read characters so far after you have input the string.
Then you subtract this number by one and use it as an index to command:
char command[6];
int n = 0;
if (fscanf(stdin, "%5[^\n]" "%n", command, &n) != 1)
{
fputs("Error at input!", stderr);
// error routine.
}
getchar();
if (n != 0)
{
char last_letter = command[n-1];
}
#include <stdio.h>
int main (void)
{
char command[6];
int n = 0;
if (fscanf(stdin, "%5[^\n]" "%n", command, &n) != 1)
{
fputs("Error at input!", stderr);
// error routine.
}
getchar();
if (n != 0)
{
char last_letter = command[n-1];
putchar(last_letter);
}
return 0;
}
Execution:
./a.out
hello
o

Tokenize Strings using Pointers in ANSI C

This is in Ansi C. I am given a string. I am supposed to create a method that returns an array of character pointers that point to the beginning of each word of said string. I am not allowed to use Malloc, but instead told that the maximum length of input will be 80.
Also, before anyone flames me for not searching the forum, I can't use strtok :(
char input[80] = "hello world, please tokenize this string"
and the output of the method should have 6 elements;
output[0] points to the "h",
output[1] points to the "w",
and so on.
How should I write the method?
Also, I need a similar method to handle input from a file with maximum of 110 lines.
Pseudocode:
boolean isInWord = false
while (*ptr != NUL character) {
if (!isInWord and isWordCharacter(*ptr)) {
isInWord = true
save ptr
} else if (isInWord and !isWordCharacter(*ptr)) {
isInWord = false
}
increment ptr
}
isWordCharacter checks whether the character is part of the word or not. Depending on your definition, it can be only alphabet character (recognize part-time as 2 words), or it may include - (recognize part-time as one word).
Because it's homework here's a part of what you might need:
char* readPtr = input;
char* wordPtr = input;
int wordCount = 0;
while (*readPtr++ != ' ');
/* Here we have a word from wordPtr to readPtr-1 */
output[wordCount++] = /* something... :) */
You'll need that in a loop, and must consider how to move onto the next word, and check for end of input.

How to get the start of an email address

I have two strings, one with an email address, and the other is empty.
If the email adress is e.g. "abc123#gmail.com", I need to pass the start of the email address, just before the # into the second string. For example:
first string: "abc123#gmail.com"
second string: "abc123"
I've written a loop, but it doesn't work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char email[256] = "abc123#gmail.com";
char temp[256];
int i = 0;
while (email[i] != '#')
{
temp = strcat(temp, email[i]);
i++;
}
printf ("%s\n", temp);
system ("PAUSE");
return 0;
}
Basically, I took every time one char from the email address, and added it into the new string. For example if the new string has a on it, now I'll put b with it too using strcat....
Pointers. Firstly, strcat() returns a char pointer, which C can't cast as a char array for some reason (which I hear all C programmers must know). Secondly, the second argument to strcat() is supposed to be a char pointer, not a char.
Replacing temp = strcat(temp, email[i]); with temp[i] = email[i]; should do the trick.
Also, after the loop ends, terminate the string with a null character.
temp[i] = '\0';
(After the loop ends, i is equal to the length of your extracted string, so temp[i] is where the terminal should go.)
There are better ways to solve this problem (e.g. by finding the index of the # (by strcspn or otherwise) and doing a memcpy), but your method is very close to working, so we can just make a few small adjustments.
As others have identified, the problem is with this line:
temp = strcat(temp, email[i]);
Presumably, you are attempting to copy the character at the ith position of email into the corresponding position of temp. However, strcat is not the correct way to do so: strcat copies data from one char* to another char*, that is, it copies strings. You just want to copy a single character, which is exactly what = does.
Looking at it from a higher level (so that I don't just tell you the answer), you want to set the appropriate character of temp to the appropriate character of email (you will need to use i to index both email and temp).
Also, remember that strings in C have to be terminated by '\0', so you have to set the next character of temp to '\0' after you have finished copying the string. (On this line of thought, you should consider what happens if your email string doesn't have an # in it, your while loop will keep going past the end of the string email: remember that you can tell if you are at the end of a string by character == '\0' or just using character as a condition.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char email[256] = "abc123#gmail.com";
char temp[256];
size_t i = 0;
#if 0
for (i=0; email[i] && email[i] != '#'; i++) {;}
/* at the end of the loop email[i] is either the first '#',
** or that of the terminating '\0' (aka as strlen() )
*/
#else
i = strcspn(email, "#" );
/* the return value for strcspn() is either the index of the first '#'
* or of the terminating '\0'
*/
#endif
memcpy (temp, email, i);
temp[i] = 0;
printf ("%s\n", temp);
system ("PAUSE");
return 0;
}
UPDATE: a totally different approach would be to do the copying inside the loop (I guess this was the OP's intention):
for (i=0; temp[i] = (email[i] == '#' ? '\0' : email[i]) ; i++) {;}
You may want to try using strtok()

How can I check the file extensions in c?

I'm struggling with this for several days now.
I want to create a functions that goes through a directory, pick all the files with an extension *.csv and read the data in them.
I created a function that is supposed to check each file name that is legal, by checking that the string ends with .csv. To do this, I want to go to the end of the char * array. I tried this:
char * point = file_name;
while (point != "\0"){
point += 1;
}
which goes through the char * array without finding and "\0".
If I write
*point != "\0"
The compiler warns me that I'm comparing and int to a char.
I should note that I get the filename using
dirent->d_name
point != "\0" compares a pointer to another pointer, which is not what you want.
You want to compare whatever point points to , to a char with the value 0. So use e.g.
*point != '\0';
Note, if you want to find the end of the string, you could also do
point = file_name + strlen(filename);
If you want to check whether a string ends in .csv, you could also do something like
if((point = strrchr(filename,'.')) != NULL ) {
if(strcmp(point,".csv") == 0) {
//ends with csv
}
}
EDIT : fixed formatting
You must dereference the pointer to look at the value it points at:
while (*point != '\0')
point++;
Note that the right hand side is a character literal (single quotes), not a string (double quotes). This fixes the warning you got when trying to use a string.
Also note that it's really unnecessary to find the end of the string to check this. A better approach would be:
int ends_with(const char* name, const char* extension, size_t length)
{
const char* ldot = strrchr(name, '.');
if (ldot != NULL)
{
if (length == 0)
length = strlen(extension);
return strncmp(ldot + 1, extension, length) == 0;
}
return 0;
}
Call the above with e.g. ends_with("test.foo", "foo", 3) and you will get 1 returned, if no match is found it returns 0.
This is not faster, but it's a lot clearer since it operates at a higher level, using only well-known standard string functions.
To get the extension from a file name you can use the strrchr function to get the position of the last occurrence of a character in a string:
char * end = strrchr(filename, '.');
if(strcmp(end, ".csv") == 0)
{
// Yuppee! A CSV file! Let's do something with it!
}
(And have a look at what others have said, as they address several problems with your code.)
unwind is right, you just interpreted his code wrong.
What many (especially newbies; don't want to call you such) don't notice (especially when coming from other languages like JavaScript or PHP) is, that "" and '' aren't the same in C++. (Neither are they in PHP but the difference is bigger here.)
"a" is a string - essentially a const char[2].
'a' is a single char - it's just a char.
What you want is comparing a single character to a single character, so you have to use '\0'.
If you use "\0" you essentially compare your character to the address of the essentially empty string (which is an integer).
Try single quotes not double quotes. This limits the constant to a character. Double quotes are for strings not characters. You need to dereference point using * to get to the character:
char * point = file_name;
while (*point != '\0')
++point;
Based on #unwind's answer, I propose this version to check if a name ends with a specific extension. Note that the inputs are declared as const pointers pointing to const char.
int stringEndsWith(
char const * const name,
char const * const extension)
{
size_t length = 0;
char* ldot = NULL;
if (name == NULL) return 0;
if (extension == NULL) return 0;
length = strlen(extension);
if (length == 0) return 0;
ldot = strrchr(name, extension[0]);
if (ldot != NULL)
{
return (strncmp(ldot, extension, length) == 0);
}
return 0;
}

Resources