2014-09-26 109 views
1

我使用下面的代码从二进制文件n字符复制到char*变量未定义行为:C++的IStream ::读取时

std::ifstream is ("write.abc", std::ifstream::binary); 
    if (is) { 
    // get length of file: 
    is.seekg (0, is.end); 
    int length = is.tellg(); 
    is.seekg (0, is.beg); 

    char * buffer = new char [length]; 

    std::cout << "Reading " << length << " characters... "; 
    // read data as a block: 
    is.read (buffer,length); 
    std::cout << "length of buffer: "<<strlen(buffer) <<endl; 
    std::cout << "Content of buffer: "<<buffer <<endl; 
....... 

我的文件的内容:

enter image description here

这是汇编的结果: enter image description here

我的任务离子如下:我等着有:缓冲的13

内容:

缓冲区的长度abcdefghjklmn

能有一个人,请帮我解释结果?

+2

你忘了你的“弦”这是strlen的使用,以确定该字符串的结尾的结尾\ 0分配空间。 – Borgleader 2014-09-26 14:29:28

+2

您需要分配length + 1个字符,并在读取后在'buffer [length]'中放入'\ 0'。 – Kiroxas 2014-09-26 14:29:30

+0

如果您只想遍历缓冲区,您也可以使用[begin,end]方法,其中'end = begin + length + 1' – Felics 2014-09-26 14:40:56

回答

8

您的缓冲区不会终止 - 您需要分配一个额外的字符并将其设置为'\0',否则它只是一个未终止的C字符串,所以strlen将最有可能返回一个无效的值,并尝试打印通常会产生串垃圾。

char * buffer = new char [length + 1]; // <<< allocate space for `length` 
             //  characters + terminator 

std::cout << "Reading " << length << " characters... "; 
// read data as a block: 
is.read (buffer,length); 
buffer[length] = '\0'; // <<< terminate C-style string 

注意使用正确的C++ - 风格std::string!而非老派C-风格char *字符串避免这个问题和其他常见的问题,并且通常是简单,很多更稳健。如果您正在阅读二进制数据而不是文本,请考虑使用std::vector<unsigned char>

+2

同意。您可以打印var'length'并将其与strlen(buffer)的输出进行比较。他们可能不会匹配。 strlen搜索第一个NULL字节\ 0。它只是在30个字节之后找到一个 – 2014-09-26 14:31:19

+1

使用'std :: string'在这里可能不起作用。 – 2014-09-26 14:39:08

+0

@JamesKanze:哦 - 我错过了什么 - 为什么不呢? – 2014-09-26 14:40:34

1

请注意,这不是回答这个问题。这是对意见的后续处理

一种解决方案是将缓冲区“存储”为[begin, end)范围,而不是以空值终止的C字符串。与空终止字符串相比,这具有很大的优势 - 它可以与STL算法一起使用,而无需调用strlen(...)来查找end元素。

下面一些例子:

std::stringstream is("some text\n"); 

if (is) 
{ 
    // get length of file: 
    is.seekg (0, is.end); 
    auto length = is.tellg(); 
    is.seekg (0, is.beg); 

    char* begin = new char [length]; 
    char* end = begin + length; 

    std::cout<<"Reading "<<length<<" characters...\n"; 
    // read data as a block: 
    is.read (begin,length); 

    //print the data: 
    std::copy(begin, end, std::ostream_iterator<char>(std::cout)); 

    //print the data backwards: 
    std::copy(std::reverse_iterator<char*>(end), std::reverse_iterator<char*>(begin), std::ostream_iterator<char>(std::cout)); 
    std::cout<<std::endl; 

    //create string from data: 
    std::string str(begin, end); 
    std::cout<<str; 

    //sum the characters 
    auto sum = std::accumulate(begin, end, 0); 
    std::cout<<sum<<std::endl; 

    //make them uppercase 
    std::transform(begin, end, begin, toupper); 
    std::copy(begin, end, std::ostream_iterator<char>(std::cout)); 
}