Автоматическая переменная переименования файлов — const изменяет ее значение?

Вопрос:

Я вроде как noob с пространством имен std, и я пишу код, который перебирает все файлы jpeg в каталоге и удаляет любые восклицательные знаки. Я пытаюсь использовать std::string и std::vector. Моя проблема в том, что моя переменная tempname: const char tempname = (char) *filelist[j].c_str(); изменяется по мере изменения строк в filelist (что не должно быть — это постоянная переменная. Вот мясо моей функции WinMain:

std::vector<std::string> filelist;
if (!dirExists(directory)) //checks if a directory exists
{
CreateDirectory("resized", NULL);
}
std::vector<std::string> filelist = findFiles("*.jpg"); //finds files in its directory with a given extension
int result; //for rename function
for (unsigned int j=0; j< filelist.size(); j++)
{
std::string::size_type pos = filelist[j].find("!"); //check for exclamation points
if (std::string::npos != pos) //found one at index "pos" in the string
{
switch (MessageBox(window, (LPCSTR)filelist[j].c_str(), "Illegal filename - Rename?", MB_YESNO)) //user input
{
case IDYES:
{
const char tempname = (char) *filelist[j].c_str(); //the problem
//attempt to remove the exclamation point
result = rename(&tempname, filelist[j].erase(pos, 1).c_str());
if (result == 0)
MessageBox(window, "Renamed File", "Information", MB_OK);
else
MessageBox(window, "Error renaming file", "Error", MB_OK);
break;
}
case IDNO:
{
break;
}
}
}
}

Предполагая, что имя файла содержит не более одного восклицательного знака. Если бы я определял tempname как const char* это имело бы смысл, потому что это было бы значение указателя — tempname могло бы измениться без нарушения объявления const если бы данные, на которые он указал, изменились. Но, убрав указатель, я сбился с толку.

Лучший ответ:

Вы понимаете, что ваше объявление tempname означает, что вы будете копировать ровно один символ? Я почти уверен, что не то, что вы хотели.

Вероятно, вы захотите сделать копию самой строки и изменить код следующим образом:

            std::string const tempname = filelist[j];
            //attempt to remove the exclamation point
            result = rename(tempname.c_str(), filelist[j].erase(pos, 1).c_str());

Что касается того, почему ваша предположительная константная переменная меняет свое значение, если вы манипулируете базовой строкой, помните, что в вашем исходном объявлении tempname является указателем на значение, и все, что вы сказали, это то, что указатель не меняет его значение. Который это не сделал, но сделал это.

Кроме того, при объединении c_str со строковыми манипуляциями вы в значительной степени входите в опасную территорию. Посмотрите на документацию для c_str здесь, она четко заявляет, что вызов мутирующих функций-членов в строковом объекте может привести к аннулированию результата вызова c_str(). Вы должны быть осторожны при смешивании std :: string с идиомами строки C.

Ответ №1

Код, который вы публикуете, имеет неопределенное поведение, а это значит, что все может произойти. Вы определяете один char, tempname, а затем передаете его адрес для rename. rename требует указателя на строку с завершением '\0'; единственной законной символьной строкой будет пустая строка.

Если вы замените определение tempname на:

char const* tempname = filelist[j].c_str();

то у вас есть неопределенное поведение позже; вызов filelist[j].erase делает недействительным этот указатель. На практике он фактически будет указывать на данные в filelist[j] (поскольку на самом деле erase не будет перераспределено, и поэтому tempname будет продолжать указывать на первый символ в filelist[j]).

То, что вы, вероятно, хотите, это что-то вроде:

std::string newName( filelist[j] );
newName.erase( std::remove( newName.begin(), newName.end(), '!' ),
newName.end() );
result = rename( filelist[j].c_str(), newName.c_str() );
filelist[j] = newName;

Оцените статью
TechArks.Ru
Добавить комментарий