这里是文本文件的内容:
SQUARE 2
SQUARE
RECTANGLE 4 5
我试图找出为什么我的strtok()循环不会结束2ND" SQUARE"只是让长度= 0.不要完全理解strtok背后的概念,我不会介意关于strtok的讲座。这是代码:
#include <cstring>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
#include<iomanip>
using std::setprecision;
#include <fstream>
using std::ifstream;
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
ifstream fin;
fin.open("geo.txt"); // open a file
if (!fin.good())
return 1; // exit if file not found
//PI
float pi = 3.14159265359;
//DIMENSIONS
float length, width, height, radius;
//AREAS, PERIMETERS, VOLUMES
float areaSquare, periSquare;
float areaRectangle, periRectangle;
float areaCube, volCube;
float areaPrism, volPrism;
float areaCircle, periCircle;
float areaCylinder, volCylinder;
// read each line of the file
while (!fin.eof())
{
// read an entire line into memory
char buf[MAX_CHARS_PER_LINE];
fin.getline(buf, MAX_CHARS_PER_LINE);
// parse the line into blank-delimited tokens
int n = 0; // a for-loop index
// array to store memory addresses of the tokens in buf
const char* token[MAX_TOKENS_PER_LINE] = {0}; // initialize to 0
// parse the line
token[0] = strtok(buf, DELIMITER); // first token
if (token[0]) // zero if line is blank
{
for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
{
token[n] = strtok(0, DELIMITER); // subsequent tokens
if (!token[n] || token[n]==0) break;
}
}
if(strcmp("SQUARE", token[0]) == 0) //1
{
length = atof(token[1])?atof(token[1]):0;
areaSquare = length * length;
periSquare = 4 * length;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "Area=" << areaSquare << ' ';
cout << "Perimeter=" << periSquare << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else if(strcmp("RECTANGLE", token[0]) == 0) //2
{
length = atof(token[1])?atof(token[1]):0;
width = atof(token[2])?atof(token[2]):0;
areaRectangle = length * width;
periRectangle = 2 * length + 2 * width;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "width=" << token[2] << ' ' ;
cout << "Area=" << areaRectangle << ' ';
cout << "Perimeter=" << periRectangle << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else
{
cout << "End of program. Press ENTER to exit.";
cin.ignore(1000,10);
break;
}
}
}
答案 0 :(得分:2)
这是一个有效的版本。
主要区别在于,
我认为它们是唯一的差异,但使用diff实用程序进行检查。
#include <cstring>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
#include<iomanip>
using std::setprecision;
#include <fstream>
using std::ifstream;
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
char *tok;
ifstream fin;
fin.open("geo.txt"); // open a file
if (!fin.good())
return 1; // exit if file not found
//PI
float pi = 3.14159265359;
//DIMENSIONS
float length, width, height, radius;
//AREAS, PERIMETERS, VOLUMES
float areaSquare, periSquare;
float areaRectangle, periRectangle;
float areaCube, volCube;
float areaPrism, volPrism;
float areaCircle, periCircle;
float areaCylinder, volCylinder;
// read each line of the file
while (!fin.eof())
{
// read an entire line into memory
char buf[MAX_CHARS_PER_LINE];
fin.getline(buf, MAX_CHARS_PER_LINE);
// parse the line into blank-delimited tokens
int n = 0; // a for-loop index
// array to store memory addresses of the tokens in buf
// const char* token[MAX_TOKENS_PER_LINE] = {0}; // initialize to 0
char token[MAX_TOKENS_PER_LINE][20];
for (n=0;n<MAX_TOKENS_PER_LINE;n++)
{
token[n][0] = NULL;
}
// parse the line
tok = strtok(buf, DELIMITER); // first token
if (tok == NULL)
break;
strcpy(token[0],tok);
if (token[0]) // zero if line is blank
{
for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
{
tok = strtok(NULL, DELIMITER); // subsequent tokens
if (tok == NULL)
break;
strcpy(token[n],tok);
// if (!token[n] || token[n]==0) break;
}
}
if(strcmp("SQUARE", token[0]) == 0) //1
{
length = atof(token[1])?atof(token[1]):0;
areaSquare = length * length;
periSquare = 4 * length;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "Area=" << areaSquare << ' ';
cout << "Perimeter=" << periSquare << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else if(strcmp("RECTANGLE", token[0]) == 0) //2
{
length = atof(token[1])?atof(token[1]):0;
width = atof(token[2])?atof(token[2]):0;
areaRectangle = length * width;
periRectangle = 2 * length + 2 * width;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "width=" << token[2] << ' ' ;
cout << "Area=" << areaRectangle << ' ';
cout << "Perimeter=" << periRectangle << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else
{
cout << "End of program. Press ENTER to exit.";
cin.ignore(1000,10);
break;
}
}
}
答案 1 :(得分:2)
您的分段错误是由此引起的:
length = atof(token[1])?atof(token[1]):0;
你犯了假设令牌[1]被标记化的错误。如果你看看你的第二个“SQUARE”,你会发现对于那一行,它会将token [1]设置为NULL。然后将NULL传递给atof(),这可以理解为错误。
你也正在使用strtok()。没有理由从结果中strcpy(),因为strtok()本身是一个破坏性的操作。
所以这是关于strtok的讲座。
首先,它是邪恶的,但有时候你还是有用的。标记符可能是一个痛苦的写入。
strtok背后的想法是创建一个简单的标记化器。一个令牌器是一个很难写的屁股,如果你不介意让你的计算机真的很容易用它,它的界面实际上相当不错。例如,您可以使用非常少量的代码来解析命令行参数。
然而,strtok对您使用它的字符串具有破坏性。它将使用0替换它找到的标记,自动为null终止返回的值。这意味着您可以直接使用返回的字符串而无需复制它。像这样的字符串:
here are spaces0
改为
here0are0spaces0
其中0分隔字符串字符(0)的结尾。这是在适当的位置完成的,你可以获得指向这里,是和空格的指针。
strtok使用静态变量 - 意味着它在调用之间保留状态信息。在第一次调用时,你传递一个指向你想要标记的字符串的指针;从那时起,你传递一个NULL指针,指示你希望它在之前停止的地方继续。它返回下一个标记,当它找到字符串的结尾时返回NULL。
strtok循环很容易编写。此代码将为您正确地标记字符串。以下示例代码很难看,但我责备累了。
char *input_string; // Just so we have it
const int MAX_TOKENS = 10; // Arbitrary number
char *tokens[MAX_TOKENS]; // This will do all the storage we need.
tokens[0] = strtok(input_string, " -=\""); // Setup call.
int number_of_tokens = 1; // We've already filled tokens[0], so we have 1 token. We want to start filling from 1.
do {
if (tokens[number_of_tokens] = strtok(NULL," -=\"")) number_of_tokens++;
else break;
} while(number_of_tokens < MAX_TOKENS);
循环中的第一行是C程序员的常见做法,但对于可读性而言是丑陋的。这是它的作用:
a)它将标记[number_of_tokens]设置为strtok的返回值。 b)如果为NULL,则终止循环(第二行)。 addendnum:有一个内联测试。您可以执行if(a = 1)并且它将返回true并将a设置为1.您可以执行if(a = 0),当将a设置为0时,它将返回false。如果strtok(此行)利用了这一事实)返回NULL,嗯,这是假的。 c)如果不是NULL,则令牌[number_of_tokens]现在包含指向字符串中找到的下一个令牌的指针。 d)由于找到了令牌,因此令牌的数量(number_of_tokens)递增。 5)它重用了一个变量,该变量保留了有多少令牌的数量,作为它保留的指针数组的索引。 6)它无限循环,直到它满足strtok返回NULL的条件,或者while()条件(在这种情况下,有超过10个令牌)。
如果给出了这个字符串:
这里有一些= words0
这将是
*tokens[0]="here"
*tokens[1]="are"
*tokens[2]="some"
*tokens[3]="words"
*tokens[4] = NULL
number_of_tokens = 4
正如您所看到的,不需要复制任何内容,因为该字符串在内存中被替换为:
here0are0some0words0
其中0分隔字符串字符(0)的结尾。
我希望这能回答你的问题。
答案 2 :(得分:0)
确定。当你的行
const char* token[MAX_TOKENS_PER_LINE] = {0};
创建一个指针数组,但它们都没有指向任何东西。第一个元素设置为0(这是一个NULL地址),其余元素未初始化。当您运行并处理第2行(其中包含1个元素)时,令牌[0]指向“SQUARE”但令牌[1]的值为0x00(NULL)。这是一个无效的内存位置。然后使用行
处理令牌[1]length = atof(token[1])?atof(token[1]):0;
这会导致分段错误,因为token [1]是一个NULL指针。在我的版本中,token [1]是一个指向NULL字符串的有效指针,它将长度设置为0.我建议你使用-g标志进行编译(例如g ++ -g test.cpp -o test)。然后调用'gdb test'并使用break,run,continue命令逐步执行代码。您可以使用print命令显示变量的内容。
在gdb的第一次运行中输入'run'。这将失败,然后输入'bt',它将告诉你失败的行,让我们称之为亚麻。
在第二次运行中输入'break linenumber'然后'run',执行将在失败的行上停止但在执行之前。然后,您可以查看变量的内容,这些内容将为您提供有关失败原因的重要线索。
答案 3 :(得分:0)
以下是一些与您的代码密切相关的工作C ++。
我修改了I / O处理; fin.getline()
报告是否有线,因此应该用它来控制循环;我估计fin.eof()
是一个红旗警告(C中为feof(fp)
。)
发生核心转储是因为您没有检查单词SQUARE
后面是否有长度标记。修改后的代码检查它是否获得了正确的令牌数量,如果没有则抱怨。使用strtok()
的代码已统一为一个循环;它包含一个诊断打印语句,显示刚刚找到的令牌(有助于检查发生了什么)。
我删除了一堆未使用的变量;每个变量都在计算块中定义和初始化。
在C ++中使用C字符串和strtok()
有很多可能的保留(如果所有代码都使用C标准I / O函数(如{{1}编写),则打印会更加简洁})。您可以在Strange strtok()
error找到printf()
替代方案的讨论。您可以在Reading user input and checking the string找到关于strtok()
在库函数中发生灾难的原因的另一个讨论。
strtok()