Get file name without extension? - c++

I'm new to C++ world, I stuck with a very trivial problem i.e. to get file name without extension.
I have TCHAR variable containing sample.txt, and need to extract only sample, I used PathFindFileName function it just return same value what I passed.
I tried googling for solution but still no luck?!
EDIT: I always get three letter file extension, I have added the following code,
but at the end I get something like Montage (2)««þîþ how do I avoid junk chars at the end?
TCHAR* FileHandler::GetFileNameWithoutExtension(TCHAR* fileName)
{
int fileLength = _tcslen(fileName) - 4;
TCHAR* value1 = new TCHAR;
_tcsncpy(value1, fileName, fileLength);
return value1;
}

Here's how it's done.
#ifdef UNICODE //Test to see if we're using wchar_ts or not.
typedef std::wstring StringType;
#else
typedef std::string StringType;
#endif
StringType GetBaseFilename(const TCHAR *filename)
{
StringType fName(filename);
size_t pos = fName.rfind(T("."));
if(pos == StringType::npos) //No extension.
return fName;
if(pos == 0) //. is at the front. Not an extension.
return fName;
return fName.substr(0, pos);
}
This returns a std::string or a std::wstring, as appropriate to the UNICODE setting. To get back to a TCHAR*, you need to use StringType::c_str(); This is a const pointer, so you can't modify it, and it is not valid after the string object that produced it is destroyed.

You can use PathRemoveExtension function to remove extension from filename.
To get only the file name (with extension), you may have first to use PathStripPath, followed by PathRemoveExtension.

Try below solution,
string fileName = "sample.txt";
size_t position = fileName.find(".");
string extractName = (string::npos == position)? fileName : fileName.substr(0, position);

TCHAR* FileHandler::GetFileNameWithoutExtension(TCHAR* fileName)
{
int fileLength = _tcslen(fileName) - 4;
TCHAR* value1 = new TCHAR[fileLength+1];
_tcsncpy(value1, fileName, fileLength);
return value1;
}

Try this:
Assuming the file name is in a string.
string fileName = your file.
string newFileName;
for (int count = 0;
fileName[count] != '.';
count++)
{
newFileName.push_back(fileName[count]);
}
This will count up the letters in your original file name and add them one by one to the new file name string.
There are several ways to do this, but this is one basic way to do it.

Related

String formation from hex string

I m trying to create hash of file by reading its content, and then hashing it using SHA1, after this I m trying to create a filename with this hash value, but the issue only first two char of hash string is taken.
Hash function
void hash(const std::string& content, std::string& retValue, bool isFile)
{
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, content.c_str(), content.size());
if (isFile) {
FILE* f = fopen(content.c_str(),"r");
assert(f != NULL);
char buffer[1024]={0};
while(fgets(buffer,1024,f))
SHA1_Update(&ctx,buffer,strlen(buffer));
fclose(f);
}
unsigned char hash[SHA_DIGEST_LENGTH] = {0};
SHA1_Final(hash,&ctx);
retValue.resize(60);
int cnt = 0;
for(int i=0;i<SHA_DIGEST_LENGTH;i++){
char ch[3] = {0};
sprintf(ch,"%02x",hash[i]);
retValue[cnt++] = ch[0];
retValue[cnt++] = ch[1];
retValue[cnt++] = ch[2];
}
std::cout<<retValue<<std::endl;
}
when I print hash value in console I get a valid looking val such as this 51d946cde43e90ec03493f88914ca948b5215916, but it contains only first two characters in this case '51', I switched to gdb to view the string, gdb shows me the string in a weird way 51\000d9\000\064\066\000cd\000e4\000\063e\000\071\060\000ec\000\060\063\000\064\071\000\063f\000\070\070\000\071\061\000\064c\000a9\000\064\070\000b5\000\062\06 1\000\065\071\000\061\066. I am saving file with std::ofstream. I have looked at prev answers that said this is a bug in gdb so it prints like this, but here I can see file getting created doesnt with only first char of hash.
Why is this happening?
Thank You.
-----------------------------------------------EDIT-------------------------------------------------------------------------
From comment of dvix, it got resolved, I was copying '\0' with ch[2], so basically I was needed to only copy first two character from each hex character, ch[0] and ch[1] and then place null at very end of string.
so the code inside of for loop should look like this
for(int i=0;i<SHA_DIGEST_LENGTH;i++){
char ch[3] = {0};
sprintf(ch,"%02x",hash[i]);
retValue[cnt++] = ch[0];
retValue[cnt++] = ch[1];
}
retValue[cnt] = '\0';
Here's a slightly shorter version that does the same thing:
for(int i=0;i<SHA_DIGEST_LENGTH;i++){
sprintf(retValue + cnt,"%02x",hash[i]);
cnt += 2;
}

C++ std::string length() or size() not working on method args

I have a method that that creates a MatLab array name from a file path and returns it as a std::string. the resulting string is passed into another method that writes the name to the file. When I try to get the length of the passed in string, it displays 0 when the length of the string is 12 or 13 chars.
My code:
bool MyClass::masterMethod(std::string fileURI){
FILE* dataStream;
// Assume dataStream is set up correctly
// Get arrayName from File URI
std::string arrayName = this->makeArrayNameFromPath( fileURI);
//Write array name to file
this->writeArrayName(arrayName , dataStream)
}
std::string MyClass::makeArrayNameFromPath(std::string filePathURI){
std::string tempString = filePathURI.substr(filePathURI.find_last_of('/')+1);
std::string returnString = "";
long index = 0;
for(long i = 0; i < tempString.length(); i++){
if((tempString[i] != ' ') && (tempString[i] != '.')){
returnString[index++] = tempString[i];
}
}
return returnString;
}
void MyClass::writeArrayName(std::string name , FILE *nameStream){
// long testLength = name.length();
// long testLength2 = name.size();
// const char* testChar = nam.c_str();
// long testCharLen = strlen(testChar);
// The size of the name is the number of Chars * sizeof(int8_t)
int32_t sizeOfName = (int32_t)(name.length() * sizeof(int8_t));
int32_t nameType = miINT8;
fwrite(&nameType , sizeof(int32_t) , 1 , nameStream);
fwrite(&sizeOfName, sizeof(sizeOfName), 1, nameStream);
fwrite(&name , sizeof(name[0]), sizeOfName , nameStream);
}
So I'm not sure why string::length is not working. If a create a std::string test = name, and print it , I can get the value of the string but can not get its length or size.
If I use const char* testName = name.c_str(); long test = strlen(testName), I get a the
correct value, but thought that wasn't necessary.
So any advice or suggestion is appreciated.
returnString[index++] = tempString[i]; doesn't do what you think it does. It's not adding additional space or length to the string, only overwriting memory at a location that the string doesn't actually own. I think returnString.append(1, tempString[i]) or similar should do it.
You never give the string a size, just trying to assign positions that isn't there.
Try this instead to add characters to the return value
returnString += tempString[i];

Find a char or substring with specifying start pos

I couldn't find a function which would let me specify the start pos for beginning a char or substring search.
I have, for example:
char *c = "S1S2*S3*S4";
I'd like search for 'S3' by searching the first '*' asterisk and then the second asterisk following it and finally getting the substring 'S3' enclosed by those asterisks.
The string class has a large find family of functions that take an index as a second argument. Repeated applications of find('*', index) should get you what you need.
std::string s(c);
std::string::size_type star1 = s.find('*');
std::string::size_type star2 = s.find('*', star1 + 1);
std::string last_part = s.substr(star2 + 1);
One solution would be to find the location of the first asterisk, then the location of the second asterisk. Then use those positions as the start and end locations to search for S3.
Use
char *strchr( const char *str, int ch );
See here for reference
#include <string>
std::string between_asterisks( const std::string& s ) {
std::string::size_type ast1 = s.find('*');
if (ast1 == std::string::npos) {
throw some_exception();
}
std::string::size_type sub_start = ast1+1;
std::string::size_type ast2 = s.find('*', sub_start);
if (ast2 == std::string::npos) {
throw some_exception();
}
return s.substr(sub_start, ast2-sub_start);
}
You can use strchr(). Simply save the returned pointer and pass it to the next call. As this pointer points to the occurence of your search, the search will start from there.
well one possibility - if you are to use c-style char* arrays for strings - is to use strchr to search for the occurrences of the asterisks, e.g., (and with NO error checking, mind)
char c []= "S1S2*S3*S4";
char* first = strchr(c,'*');
if (first) {
char* start = ++first;
char* nextast = strchr(start,'*');
char* s3str = new char[nextast-start+1];
strncpy(s3str,start,nextast-start);
s3str[next-start] = '\0';
}
But it would be easier to use the C++ string class to do this.

Newline character in Text Document?

I wrote a pretty simple function that reads in possible player names and stores them in a map for later use. Basically in the file, each line is a new possible player name, but for some reason it seems like all but the last name has some invisible new line character after it. My print out is showing it like this...
nameLine = Georgio
Name: Georgio
0
nameLine = TestPlayer
Name: TestPlayer 0
Here is the actual code. I assume I need to be stripping something out but I am not sure what I need to be checking for.
bool PlayerManager::ParsePlayerNames()
{
FileHandle_t file;
file = filesystem->Open("names.txt", "r", "MOD");
if(file)
{
int size = filesystem->Size(file);
char *line = new char[size + 1];
while(!filesystem->EndOfFile(file))
{
char *nameLine = filesystem->ReadLine(line, size, file);
if(strcmp(nameLine, "") != 0)
{
Msg("nameLine = %s\n", nameLine);
g_PlayerNames.insert(std::pair<char*, int>(nameLine, 0));
}
for(std::map<char*,int>::iterator it = g_PlayerNames.begin(); it != g_PlayerNames.end(); ++it)
{
Msg("Name: %s %d\n", it->first, it->second);
}
}
return true;
}
Msg("[PlayerManager] Failed to find the Player Names File (names.txt)\n");
filesystem->Close(file);
return false;
}
You really need to consider using iostreams and std::string. The above code is SO much more simpler if you used the C++ constructs available to you.
Problems with your code:
why do you allocate a buffer for a single line which is the size of the file?
You don't clean up this buffer!
How does ReadLine fill the line buffer?
presumably nameLine points to the begining of the line buffer, if so, given in the std::map, the key is a pointer (char*) rather than a string as you were expecting, and the pointer is the same! If different (i.e. somehow you read a line and then move the pointer along for each name, then std::map will contain an entry per player, however you'll not be able to find an entry by player name as the comparison will be a pointer comparison rather than a string comparison as you are expecting!
I suggest that you look at implementing this using iostreams, here is some example code (without any testing)
ifstream fin("names.txt");
std::string line;
while (fin.good())
{
std::getline(fin, line); // automatically drops the new line character!
if (!line.empty())
{
g_PlayerNames.insert(std::pair<std::string, int>(line, 0));
}
}
// now do what you need to
}
No need to do any manual memory management, and std::map is typed with std::string!
ReadLine clearly includes the newline in the data it returns. Simply check for and remove it:
char *nameLine = filesystem->ReadLine(line, size, file);
// remove any newline...
if (const char* p_nl = strchr(nameLine, '\n'))
*p_nl = '\0';
(What this does is overwrite the newline character with a new NUL terminator, which effectively truncates the ASCIIZ string at that point.
Most likely the ReadLinefunction also reads the newline character. I suppose your file does not have a newline at the very last line, thus you do not get a newline for that name.
But until I know what filesystem, FileHandle_t, and Msg is, it is very hard to determine where the issue could be.

Splitting the string at Enter key

I'm getting the text from editbox and I'd want to get each name separated by enter key like the character string below with NULL characters.
char *names = "Name1\0Name2\0Name3\0Name4\0Name5";
while(*names)
{
names += strlen(names)+1;
}
how would you do the same for enter key (i.e separated by /r/n) ? can you do that without using the std::string class?
Use strstr:
while (*names)
{
char *next = strstr(names, "\r\n");
if (next != NULL)
{
// If you want to use the key, the length is
size_t len = next - names;
// do something with a string here. The string is not 0 terminated
// so you need to use only 'len' bytes. How you do this depends on
// your need.
// Have names point to the first character after the \r\n
names = next + 2;
}
else
{
// do something with name here. This version is 0 terminated
// so it's easy to use
// Have names point to the terminating \0
names += strlen(names);
}
}
One thing to note is that this code also fixes an error in your code. Your string is terminated by a single \0, so the last iteration will have names point to the first byte after your string. To fix your existing code, you need to change the value of names to:
// The algorithm needs two \0's at the end (one so the final
// strlen will work and the second so that the while loop will
// terminate). Add one explicitly and allow the compiler to
// add a second one.
char *names = "Name1\0Name2\0Name3\0Name4\0Name5\0";
If you want to start and finish with a C string, it's not really C++.
This is a job for strsep.
#include <stdlib.h>
void split_string( char *multiline ) {
do strsep( &multiline, "\r\n" );
while ( multiline );
}
Each call to strsep zeroes out either a \r or a \n. Since only the string \r\n appears, every other call will return an argument. If you wanted, you could build an array of char*s by recording multiline as it advances or the return value of strsep.
void split_string( char *multiline ) {
vector< char* > args;
do {
char *arg = strsep( &multiline, "\r\n" );
if ( * arg != 0 ) {
args.push_back( arg );
}
} while ( multiline );
}
This second example is at least not specific to Windows.
Here's a pure pointer solution
char * names = "name1\r\nName2\r\nName3";
char * plast = names;
while (*names)
{
if (names[0] == '\r' && names[1] == '\n')
{
if (plast < names)
{
size_t cch = names - plast;
// plast points to a name of length cch, not null terminated.
// to extract the name use
// strncpy(pout, plast, cch);
// pout[cch] = '\0';
}
plast = names+2;
}
++names;
}
// plast now points to the start of the last name, it is null terminated.
// extract it with
// strcpy(pout, plast);
Since this has the C++ tag, the easiest would probably using the C++ standard library, especially strings and string streams. Why do you want to avoid std::string when you're doing C++?
std::istringstream iss(names);
std::string line;
while( std::getline(iss,line) )
process(line); // do process(line.c_str()) instead if you need to