1

我正在尝试使我的代码能够将文件分隔到客户数据库中(它由许多空格而不是制表符分隔)。我尝试使用 strtok,但出现 EXC_BAD_ACCESS 错误。这是我的 main.cpp 代码:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include "Cust.h"
using namespace std;

int main (int argc, char * const argv[]) {
    Cust customers[500];
 int idx = 0;
 string tmpString = "";
 string tmpAcctFN = "";
 string tmpAcctLN = "";
 ifstream input("P3_custData.txt");
 while (!input.eof()){
  getline(input,tmpString);
  tmpString.insert(0,"");
  customers[idx].setAcctNum(atoi(strtok((char *)tmpString.c_str()," ")));
  customers[idx].setAcctFN(strtok(NULL," "));
  customers[idx].setAcctLN(strtok(NULL," "));
  //customers[idx].setCurrBalance(atof(strtok((char *) tmpString.c_str()," ")));
 }
 cout << "return 0;";
    return 0;
}

根据评论进行更改后,我仍然得到 EXC_BAD_ACCESS:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include "Cust.h"
using namespace std;

int main (int argc, char * const argv[]) {
    Cust customers[500];
    int idx = 0;
    string tmpString = "";
    string tmpAcctFN = "";
    string tmpAcctLN = "";
    char * s;
    ifstream input("P3_custData.txt");
    while (!input.eof()){
        getline(input,tmpString);
        s = strdup (tmpString.c_str());
        customers[idx].setAcctNum(atoi(strtok(s," ")));
        customers[idx].setAcctFN(strtok(NULL," "));
        customers[idx].setAcctLN(strtok(NULL," "));
        //customers[idx].setCurrBalance(atof(strtok((char *) tmpString.c_str()," ")));
    }
    cout << "return 0;";
    return 0;
}
4

3 回答 3

2

试图修改std::string::c_str()方法返回的字符串是非法的。strtok将进行这样的尝试(您必须放弃返回字符串的常量这一事实是一个死的赠品)。换句话说,你不能使用strtok.std::string::c_str()

要么摆脱strtok(更好),要么创建字符串的独立可修改副本并strtok在其上使用(更糟)。

于 2009-11-25T01:21:05.413 回答
0

c_str方法将返回一个指向const数据的指针。一种方法是替换:

customers[idx].setAcctNum(atoi(strtok((char *)tmpString.c_str()," ")));
customers[idx].setAcctFN(strtok(NULL," "));
customers[idx].setAcctLN(strtok(NULL," "));

和:

char *s = strdup (tmpString.c_str());
// check if s is NULL
customers[idx].setAcctNum(atoi(strtok(s," ")));
customers[idx].setAcctFN(strtok(NULL," "));
customers[idx].setAcctLN(strtok(NULL," "));
free (s);

这为您提供了一个可以运行的重复的可写strtok字符串。

我通常不喜欢在 C++ 代码中使用旧式C 的东西,但我在让代码运行方面也很务实。很可能有一种更“C++”的做事方式。

而且,如果您的实现没有strdup(我认为它不是标准的一部分):

char *strdup (const char *s) {
    char *d = (char *)(malloc (strlen (s) + 1));
    if (d == NULL) return NULL;
    strcpy (d,s);
    return d;
}

更新:

Anthony,根据您对问题的更新,如果您仍然遇到错误,那么可能是因为该字符串不是您所期望的。想想看,你不应该写入EXC_BAD_ACCESS结果中得到 a ,c_str因为内存在技术上不是只读的。EXC_BAD_ACCESS仅当您尝试写入只读内存(或地址空间之外)时才会发生。正如一位评论者指出的那样,对于某些简单的情况,c_str 可能会返回一个指向只读内存的指针,但如果您的文件具有复杂的行,则此处不太可能出现这种情况。

在任何一种情况下,复制到可变字符串都会修复它。

我认为可能发生的是您的一个strtoks 正在返回 NULL。如果您正在处理少于三个字段的行(例如,“hello there”,其中第三个strtok将返回 NULL),就会出现这种情况。

strtok在调用之前打印出代码中的字符串:

std::cerr << "[" << s << "]" << std::endl;

(并确保在strtok调用后释放字符串,以免最终导致内存泄漏)。

如果这是问题所在,则解决方案取决于您的需要。您可以通过在调用之前检查并复制单个字符串来完全忽略这些行set...()- 换句话说,只有在所有三个字符串都不为空时才调用它们。

您可以事先检查字符串以计算分隔符的数量,确保至少有两个。

我敢肯定还有其他可能性,但我真的无能为力,直到:

  1. 我们知道问题很可疑;和
  2. 你告诉我们你想要发生什么。

希望有帮助。

于 2009-11-25T01:31:11.100 回答
0

strtok()NULL如果没有更多的令牌,将返回。如果您将该指针交给例如 a 的构造函数或赋值运算符,则必须检查这一点std::string

char* token = ::strtok(0, " ");
if(!token) {
    // handle error
}

不过,请考虑使用字符串流或其他更多类似 C++ 的功能,以避免一开始就掉入此类陷阱。

于 2009-11-25T02:41:36.730 回答