C++ file renaming best style [closed] - c++

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I have 50 image files which I want to rename.
from: img (1) - Copy.jpg
to: picture_1.jpg
Do you know more elegant way to write it? I came up with this:
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
int main()
{
string oldname = "";
string newname = "";
char oldfilename[20];
char newfilename[30];
for (int i = 1; i <= 50; i++)
{
oldname = "img ("+ to_string(i) +") - copy.jpg"; // old file name
newname = "picture_" + to_string(i) + ".jpg"; // new file name
strcpy(oldfilename, oldname.c_str());
strcpy(newfilename, newname.c_str());
if (rename(oldfilename, newfilename) != 0)
perror("Error renaming file");
else
{
cout << oldfilename << " renamed successfully to " << newfilename << endl;
}
}
char c;
cin >> c;
return 0;
}

This smells like an x y problem. Why are you reaching for C++ to do this?
If you insist on C++ (not to be confused with C), then you should avoid char arrays and strcpy and friends. In particular, strcpy is very dangerous (at least use strncpy, which can't overrun the buffer).

The code can be improved a great deal by making use of C++17 and C++20 features. In particular, formatting the filename can be done with std::format() (or use the {fmt} library if this feature is not supported by your compiler yet):
for (int i = 1; i <= 50; ++i) {
auto oldname = std::format("img ({}) - copy.jpg", i);
auto newname = std::format("picture_{}.jpg", i);
And use C++17's std::fileystem::rename() to rename files without resorting to C functions:
try {
std::filesystem::rename(oldname, newname);
} catch (std::filesystem::filesystem_error &err) {
std::cerr << std::format("Error renaming file: {}\n", err.what());
}
}
If your compiler doesn't support std::filesystem yet, you can use boost::filesystem instead.

I'm going to simplify what the asker had and apply some general best practices. For ease of comparison, I'm sticking to old school C++ code. Comments explaining what I did and why are embedded to keep the explanations close to what they explain.
#include <iostream>
#include <cstdio>
#include <string>
// using namespace std should only be used under controlled circumstances
// if at all.
int main()
{
// removed all variables
for (int i = 1; i <= 50; i++)
{
// Keep variable definitions close to usage unless you have a good
// reason not to. Can't think of any good reasons.
std::string oldname = "img ("+ std::to_string(i) +") - copy.jpg";
std::string newname = "picture_" + std::to_string(i) + ".jpg";
// no need for the char arrays or the strcpys. Instead we will use the
// std::string::c_str function to get the string's backing array
// If C++17 or newer is available we could use std::filesystem::rename.
// I'm leaving off the std:: prefix on rename because C is all we need
if (rename(oldname.c_str(), newname.c_str()) != 0)
{ // I find that always using the optional braces prevents future bugs.
// That's a personal opinion and choice.
perror("Error renaming file");
}
else
{
std::cout << oldname << " renamed successfully to " << newname << std::endl;
}
}
char c;
std::cin >> std::noskipws >> c; // >> c will ignore whitespace, so the user
// cannot advance simply by pressing enter.
return 0;
}
A note on comments. If the purpose of the code cannot be inferred from the code and the variable names, consider changing the code. Only comment the stuff that's still arcane no matter how you rework it.

Related

How iterate through char *str in C++? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 12 months ago.
Improve this question
#include <bits/stdc++.h>
using namespace std;
int main()
{
char *str;
gets(str);
int size = strlen(*(&str));
//How to iterate through this str which is acting like a string here
return 0;
}
//I'm trying to print each char in a new line.
Ignoring all the other problems, such as using an uninitialized pointer, using gets (it's so bad it's been removed from C and C++), including bits/stdc++.h, not using std::string and std::getline...
Using your size variable, you can use loop like this:
for(int index = 0 ; index < size ; ++index) {
std::cout << "character at index " << index << " is '" << str[index] << "'\n";
}
But note that your code will crash at gets and never get to this loop. Please find better learning material to get started with C++!
PS. To get your code to not crash, change char *str; to char str[10000];... Then that program should run and you are unlikely to accidentally cause a buffer overflow. Still, I repeat, get better learning material!
The character pointer str doesn't point to any char object and has not been initialized.
Second, the function gets has been deprecated in C++11 and removed in C++14.
A better way would be to use std::string instead as shown below:
#include <string>
#include <iostream>
int main()
{
std::string str;
//take input from user
std::getline(std::cin, str);
//print out the size of the input string
std::cout << str.size() << std::endl;
//iterate through the input string
for(char& element: str)
{
std::cout<<element<<std::endl;
}
}

Is there a better way to write this for C++? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
The goal is to create substrings from an inputted string by extracting words which are separated by spaces.
The substrings have to be variables themselves.
Sounds easy, but what makes it hard is that you can only use strcpy, strcmp, strlen, strcat, strncpy, strncmp, strnlen, and strncat.
Example:
input:
"John 40 Hitman"
driver:
...
cout << word1 << endl
<< word2 << endl
<< word3 << endl;
output:
John
40
Hitman
Here is my code
#include <iostream>
#include <cstring>
#include <stdio.h>
int main(){
const char *string = "Name Age Job";
char name[10];
char age[10];
char job[10];
int length = strlen(string);
int temp = 0;
bool flag = false;
for(int i = 0; i < length + 1; i++){
if(isspace(string[i]) && !flag){
strncpy(name, string, i);
name[i] = '\0';
temp = i;
flag = !flag;
cout << name << endl;
continue;
}
if(isspace(string[i])){
strncpy(age, string + temp + 1, length - i - 1);
age[temp - i] = '\0';
temp = i;
cout << age << endl;
continue;
}
if(string[i] == '\0'){
strncpy(job, string + temp + 1, length);
job[temp - i] = '\0';
cout << job << endl;
}
}
It works but it has to use a flag boolean, strings are not dynamic, only works for a string with 2 spaces, and there is a lot of repeated code. Overall really janky, but I spent roughly two hours on this and I do not know how to improve it.
If you are wondering, this is indeed a homework problem, but it's an intro class and my professor only wants the correct output for a hard coded string with only 3 words. I, however, want to learn how to improve upon it and would be really grateful for any help. Thanks.
All you need to do is replace space ' ' with '\0'(the string end) thus creating 3 substrings from the original. The following program does that and just dumps the string to cout but you could hold the pointers in an array as well (eg. char* substring[3]).
int main(){
char string[] = "Name Age Job";
char* temp = string;
for(char* it = string; *it; ++it ) {
if (*it == ' ') {
*it = '\0';
std::cout << temp << std::endl;
temp= it + 1;
}
}
std::cout << temp << std::endl;
}
The proper way to do this with C functions only would be to use strtok, although that one chops up a string in-place.
Regarding your code, there's lot of needless branching and checks. You should avoid continue, it is an almost 100% certain sign of a loop in need of improvements. Whenever you find yourself needing it, there's usually a better way.
You should also avoid strncpy because as you've noticed, it is a pain to keep track of when it null terminates and when it doesn't. It's a dangerous function and much slower than memcpy, which could be used instead here.
Here is a simplified version based on using 2 pointers, one all the time set to point at the next space and one all the time set to point at the beginning of the next valid word.
#include <string.h>
#include <ctype.h>
#include <stdio.h>
const char* find_next_space (const char* str)
{
while(*str != '\0' && !isspace(*str))
str++;
return str;
}
int main (void)
{
const char str[] = "hello world how are you";
char substr[5][10];
const char* word_start = str;
for(size_t i = 0; i<5; i++)
{
if(*word_start == '\0')
break;
const char* next_space = find_next_space(word_start);
size_t length = (size_t)(next_space-word_start);
memcpy(substr[i], word_start, length);
substr[i][length] = '\0';
puts(substr[i]);
word_start = next_space+1;
}
}
This code simplifies things by not checking if a string would fit and it doesn't allocate memory. Real production-quality C code wouldn't use char [5][10] but rather a pointer array char* [5] where each pointer is set to point at dynamically allocated memory.
I agree with #acraig5075 as your code is more C than C++.
If you are thinking of writing this in C++ using the STL string, one way of doing this is the following.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> split_string( const std::string &srcString, const char &delimiterKeywrd )
{
/**
* Splits a string according to a keyword delimiter and returns a vector with the
* segments of the split string.
*
* Args:
* #param srcString: The required string to split.
* #param delimiterKeywrd: The delimiter keyword that the string will be splitted to
* #return segmentsVectr: A vector holding the segments of the srcString that was splitted.
*
*/
std::stringstream inputStr;
inputStr.str( srcString );
std::string segment;
std::vector<std::string> segmentsVectr;
while( std::getline( inputStr, segment, delimiterKeywrd ) )
{
segmentsVectr.push_back( segment );
}
return segmentsVectr;
}
int main() {
std::string inputStr{"John 40 Hitman"};
std::vector<std::string> result;
char delimiterKeywrd = ' ';
result = split_string( inputStr, delimiterKeywrd );
// Iterate through the vector and print items on a different line.
for ( const std::string &item : result )
{
std::cout << item << std::endl;
}
return 0;
}
Check std::string at cppreference and examples if you are not familiar.
Also here I am using std::stringstream which makes it easy to use std::getline.
If std::stringstream is not your preferred way you can always check this implementation from another question about splitting a string by character.
Hope this helps.

Initialize values in an array of strings c++ [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 5 years ago.
Improve this question
Hello i'm from Indonesia. and i'm verry beginner on C++ programming. I have some problem when i learn about string on C++ . First i declared array of char and i want to initialize a value separately in different command. After i initialize the value my compiler say "Invalid Argument".
#include <iostream>
using namespace std;
int main() {
char Name[5];
Name = "Luke";
cout<<"Character 0 :"<<Name[0]<<endl;
cout<<"Character 1 :"<<Name[1]<<endl;
cout<<"Character 2 :"<<Name[2]<<endl;
cout<<"Character 3 :"<<Name[3]<<endl;
cout<<"Character 4 :"<<Name[4]<<endl;
return 0;
}
sorry if my english is bad :(
A character array(including a C string) can not have a new value assigned to it after it is declared.
The C++compiler interprets these assignment statements as attempts to change the address stored in the array name, not as attempts to change the contents of the array.
However you can use
char name[] = "Luke";
A char[] can't be assigned with a string with the = operator, except for on its initialization. That's why char Name[5]; Name = "Luke"; is invalid while char Name[5] = "Luke"; is.
Assigning strings to char[] can be done with strcpy() / memcpy()-like functions.
So you have two ways of action (assuming you want to work with char[]):
char Name[5] = "Luke";
char Name[5]; strcpy(Name, "Luke"); /* don't forget to #include <string.h>*/
Just for sake of education (since the other answers are on-point to answer the question), here's how I would have written your code to do nearly the same thing.
The changes demonstrate:
used a more appropriate container (a string instead of a char[])
checked for access overruns
moved "one unit of work" into its own subroutine
Code was compiled as C++17 with /usr/bin/clang++ -Weverything -Wno-c++98-compat --std=c++1z:
#include <cstddef>
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
void PrintCharAtPos(string const& s, string::size_type pos);
int main() {
auto Name = string{"Luke"};
PrintCharAtPos(Name, 0);
PrintCharAtPos(Name, 1);
PrintCharAtPos(Name, 2);
PrintCharAtPos(Name, 3);
PrintCharAtPos(Name, 4);
return EXIT_SUCCESS;
}
void PrintCharAtPos(string const& s, string::size_type pos) {
if (pos < s.length())
cout << "Character " << pos << " : " << s[pos] << endl;
else
cout << "Character " << pos << " : (out of bounds)" << endl;
}

Beginner: L1 Parsing Table Syntax Error [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
I'm a CS Student working on a code to implement but having hard time running it.
Can you please help me out in running this L1 Parsing Table.
May be i'm doing some stupid syntax mistake.
I'll be very thankful to your replies.
Cheers!
#include <iostream.h>
#include <conio.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
clrscr();
int i=0,j=0,k=0,m=0,n=0,o=0,o1=0,var=0,l=0,f=0,c=0,f1=0;
char str[30],str1[40]="E",temp[20],temp1[20],temp2[20],tt[20],t3[20];
strcpy(temp1,'\0');
strcpy(temp2,'\0');
char t[10];
char array[6][5][10] = {
"NT", "<id>","+","*",";",
"E", "Te","Error","Error","Error",
"e", "Error","+Te","Error","\0",
"T", "Vt","Error","Error","Error",
"t", "Error","\0","*Vt","\0",
"V", "<id>","Error","Error","Error"
};
cout << "\n\tLL(1) PARSER TABLE \n";
for(i=0;i<6;i++)
{
for(j=0;j<5;j++)
{
cout.setf(ios::right);
cout.width(10);
cout<<array[i][j];
}
cout<<endl;
}
cout << endl;
cout << "\n\tENTER THE STRING :";
gets(str);
if(str[strlen(str)-1] != ';')
{
cout << "END OF STRING MARKER SHOULD BE ';'";
getch();
exit(1);
}
cout << "\n\tCHECKING VALIDATION OF THE STRING ";
cout <<"\n\t" << str1;
i=0;
while(i<strlen(str))
{
again:
if(str[i] == ' ' && i<strlen(str))
{
cout << "\n\tSPACES IS NOT ALLOWED IN SOURSE STRING ";
getch();
exit(1);
}
temp[k]=str[i];
temp[k+1]='\0';
f1=0;
again1:
if(i>=strlen(str))
{
getch();
exit(1);
}
for(int l=1;l<=4;l++)
{
if(strcmp(temp,array[0][l])==0)
{
f1=1;
m=0,o=0,var=0,o1=0;
strcpy(temp1,'\0');
strcpy(temp2,'\0');
int len=strlen(str1);
while(m<strlen(str1) && m<strlen(str))
{
if(str1[m]==str[m])
{
var=m+1;
temp2[o1]=str1[m];
m++;
o1++;
}
else
{
if((m+1)<strlen(str1))
{
m++;
temp1[o]=str1[m];
o++;
}
else
m++;
}
}
temp2[o1] = '\0';
temp1[o] = '\0';
t[0] = str1[var];
t[1] = '\0';
for(n=1;n<=5;n++)
{
if(strcmp(array[n][0],t)==0)
break;
}
strcpy(str1,temp2);
strcat(str1,array[n][l]);
strcat(str1,temp1);
cout << "\n\t" <<str1;
getch();
if(strcmp(array[n][l],'\0')==0)
{
if(i==(strlen(str)-1))
{
int len=strlen(str1);
str1[len-1]='\0';
cout << "\n\t"<<str1;
cout << "\n\n\tENTERED STRING IS VALID";
getch();
exit(1);
}
strcpy(temp1,'\0');
strcpy(temp2,'\0');
strcpy(t,'\0');
goto again1;
}
if(strcmp(array[n][l],"Error")==0)
{
cout << "\n\tERROR IN YOUR SOURCE STRING";
getch();
exit(1);
}
strcpy(tt,'\0');
strcpy(tt,array[n][l]);
strcpy(t3,'\0');
f=0;
for(c=0;c<strlen(tt);c++)
{
t3[c]=tt[c];
t3[c+1]='\0';
if(strcmp(t3,temp)==0)
{
f=0;
break;
}
else
f=1;
}
if(f==0)
{
strcpy(temp,'\0');
strcpy(temp1,'\0');
strcpy(temp2,'\0');
strcpy(t,'\0');
i++;
k=0;
goto again;
}
else
{
strcpy(temp1,'\0');
strcpy(temp2,'\0');
strcpy(t,'\0');
goto again1;
}
}
}
i++;
k++;
}
if(f1==0)
cout << "\nENTERED STRING IS INVALID";
else
cout << "\n\n\tENTERED STRING IS VALID";
getch();
}
OUTPUT
LL(1) PARSER TABLE
NT <id> + * ;
E Te Error Error Error
e Error +Te Error
T Vt Error Error Error
t Error *Vt
V <id> Error Error Error
ENTER THE STRING :<id>+<id>*<id>;
CHECKING VALIDATION OF THE STRING
E
Te
Vte
<id>te
<id>e
<id>+Te
<id>+Vte
<id>+<id>te
<id>+<id>*Vte
<id>+<id>*<id>te
<id>+<id>*<id>e
<id>+<id>*<id>
ENTERED STRING IS VALID
[/Code]
So, you want a code inspection / code review. Here it goes:
C++ vs. C
Your program looks like it is C, but your question tag says C++. So, I'll view it from the C++ perspective.
Switch your header files to C++:
#include <iostream>
#include <string>
#include <cstdlib>
Note there is no ".h" suffix in the header names.
Console I/O
The conio.h header file is compiler specific. You didn't mention which compiler you are using, so many of us can't load your source and help you.
The clrscr() function is not necessary and often times, clearing the screen erases information that you may need later. BTW, it is not portable because not all platforms have screens.
void main
The main function returns int, always.
Variable Names
The C++ language specification allows for at least 32 characters in an identifier. More characters are allowed but only the first 32 are used to determine duplicates.
So use them. Single letters only reduce your typing time. Descriptive names allow other people, and you, to easily understand how your code is working. Do you know what all of those variable are used for, without reading any other documentation?
Style -- one declaration per line
Declare one variable per line. Easier to maintain, easier to read.
Spaces?
Spaces cost very little build time and don't effect execution speed. They add clarity, use them.
The general rules are one space between variables and operators; one space after a comma.
Character Arrays
Don't use them, the cause problems. Since your question is tagged C++, use std::string instead.
strcpy for one character
Don't use strcpy for one character, just assign it directly:
temp1[0] = '\0';
By the way, strcpy is dangerous, use strncpy instead; notice the letter 'n'.
Const
Use it. Things that don't change should be declared as const to prevent mistakes and allowing the compiler to find this mistakes. Also, character constants (a.k.a. literals) are constant and can't be changed.
Mapping Tables
Three dimensions? Really?
How about something easier to understand, such as a table of structures:
struct Token_Entry
{
const char * token_text;
unsigned int token_ID;
};
const Token_Entry token_table[] =
{
{TOKEN_NT, "NT"},
{TOKEN_ID, "<id>"},
{TOKEN_PLUS, "+"},
// ...
};
const unsigned int Number_Of_Token_Entries =
sizeof(token_table) / sizeof(token_table[0]);
Avoid gets, it's dangerous.
The gets reads input regardless of quantity. If you allocate 5 slots and the User types 10 letters, you will overrun your buffer. Very bad.
Use fgets if you must or switch to C++ and use getline(std::string).
Avoid the exit function
The proper technique is to use return in the main function. This allows proper clean up of variables. The exit function is a little bit more dangerous.
No Gotos and labels
Read up on loops in your favorite, correct, C++ text. Expecially the break and continue keywords.
One assignment per line
Carriage returns, linefeeds and newlines are free, use them. One assignment per line. It slows down the compilation process by either microseconds or nanoseconds; in otherwords, the time required to process line ending characters is negligible.
Use parenthesis
Grouping logical statements with parenthesis may not be necessary if you have memorized the precedence table, but they make the code more readable:
while ((m < strlen(str1)) && (m < strlen(str)))
Compute constants once
For example, the length of the string will not change during your analyzing loops. So store it into a constant variable:
const unsigned int length_str1 = str1.length(); // Since you will be using std::string.
const unsigned int length_str = strlen(str); // Or if you insist on C-style strings.
while ((m < length_str1) && (m < length_str))
Unsigned vs. signed integers
One of my pet peeves. Use int (a.k.a. signed int) if the value can be negative; otherwise use unsigned int. For example, a text length can't be negative. What does a string of length -5 look like?
Comment your code
Tell the reader what you are doing and why. Don't talk about assigning variables. For example, why is k incremented at the end of the loop? Why are you setting temp to null in one section but not the other. What is the f1 variable used for? Set verbosity = maximum.
Use a debugger
Since you didn't comment the code, used single letter variable names, your code will take a lot of time to understand. You can help out by using a debugger. A debugger allows you to execute lines one at a time (a.k.a. single stepping), and print out or display the values in your variables. A lot faster than the ancient art of adding print statements for your variables at different locations in your code.
Also, using a debugger is a faster method to test and evaluate your program than posting it on the web, especially here.

How to not print integer when its value is zero? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
Is there any way to make the following piece of code shorter? Maybe there is a way to use only one sprintf command, but I don't know how. I need to avoid printing x when its value is zero.
char msg[1000];
string s1 = "s1";
string s2 = "s2";
string s3 = "s3";
int x = 0;
if(x == 0)
sprintf(msg, "%s,%s,%s \n", s1.c_str(), s2.c_str(), s3.c_str());
else
sprintf(msg, "%s,%s,%s,%d \n", s1.c_str(), s2.c_str(), s3.c_str(), x);
Since you're using C++, why not use stringstream to build your buffer in pieces:
#include <cstdio>
#include <sstream>
#include <string>
using namespace std;
int main() {
string s1 = "s1";
string s2 = "s2";
string s3 = "s3";
int x = 0;
stringstream ss;
ss << s1 << "," << s2 << "," << s3;
if (x != 0)
ss << "," << x;
ss << " " << endl;
// Don't do this! See link below
//const char* c = ss.str().c_str();
string result = ss.str();
const char* c = result.c_str();
printf("Result: '%s'\n", c);
getchar();
return 0;
}
Don't let std::stringstream.str().c_str() happen to you
Since the printf family of functions evaluate but ignore any unused arguments, this would be one option;
sprintf(msg, x == 0 ? "%s,%s,%s \n" : "%s,%s,%s,%d \n",
s1.c_str(), s2.c_str(), s3.c_str(), x);
For readability and clarity, I would personally keep your current version though. Until really proven to be a problem, readability trumps micro optimization any day.
Simple, break it out into parts:
printf("%s,%s,%s", s1.c_str(), s2.c_str(), s3.c_str()); // no newline
if(x != 0)
printf(",%d", x);
printf(" \n");
When trying to do things like this, think of it as a math problem: Factor out what's in common between the two statements and do it regardless of the if conditional.
If you want to use sprintf (since you changed your question), you'll need to adjust the pointer into the buffer that you pass each time to account for what's already been written. Also, you should use snprintf which takes a length parameter, to make sure you don't over-run your buffer. This length would need adjusted after each step as well.