2014-11-21 2 views
4

При попытке прочитать в простом ANSI кодировке текстового файла в текстовом режиме (Windows) я наткнулся на странное поведение с seekg() и tellg(); Каждый раз, когда я пытался использовать tellg(), он сохранил его значение (как pos_type), а затем попытался найти его позже, я всегда буду двигаться дальше в потоке, чем там, где я остановился.Использование seekg() в текстовом режиме

В конце концов я сделал проверку здравомыслия; даже если я просто сделаю это ...

int main() 
{ 
    std::ifstream dataFile("myfile.txt", 
     std::ifstream::in); 
    if (dataFile.is_open() && !dataFile.fail()) 
    { 
     while (dataFile.good()) 
     { 
     std::string line; 
     dataFile.seekg(dataFile.tellg()); 
     std::getline(dataFile, line); 
     } 
    } 
} 

... затем, в конце концов, далее в файл линии проходят половину отсечения. Почему именно это происходит?

+0

что ком пиллер вы используете? – user657267

+0

Я использую компилятор MinGW C++. – Michael

+0

Что происходит, если вы не используете 'binary'? – 0x499602D2

ответ

3

Эта проблема вызвана libstdC++, используя разницу между текущим оставшимся буфером с lseek64, чтобы определить текущее смещение.

Буфера устанавливаются с помощью возвращаемого значения read, что для режима текстового файла на окнах возвращает количество байт, которые были введены в буфер после преобразования лицевой линии (т.е. 2 байта \r\n лицевой линии преобразуются в \n, окна также, кажется, добавляют ложную новую строку в конец файла).

lseek64 однако (что с результатами MinGW в вызове _lseeki64) возвращает текущее абсолютное положение файла и как только два значения вычитаются вы в конечном итоге со смещением, который отстоит от 1 для каждого оставшегося символа новой строки в текстовом файле (+1 для дополнительной строки перевода строки).

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

#include <iostream> 
#include <fstream> 

int main() 
{ 
    std::ifstream f("myfile.txt"); 

    for (char c; f.get(c);) 
    std::cout << f.tellg() << ' '; 
} 

Для файла с одним a характером я получаю следующие выходные данные

2 3 

Ясно охлаждает 1 для первого вызова tellg. После второго вызова позиция файла верна, поскольку конец был достигнут после учета дополнительной строки.

Помимо открытия файла в двоичном режиме, вы можете обойти проблему путем отключения буферизации

#include <iostream> 
#include <fstream> 

int main() 
{ 
    std::ifstream f; 
    f.rdbuf()->pubsetbuf(nullptr, 0); 
    f.open("myfile.txt"); 

    for (char c; f.get(c);) 
    std::cout << f.tellg() << ' '; 
} 

, но это далеко от идеала.

Надеюсь, что mingw/mingw-w64 или gcc могут это исправить, но сначала нам нужно будет определить, кто будет ответственен за его исправление. Я полагаю, что основная проблема связана с реализацией MS lysek, которая должна возвращать соответствующие значения в соответствии с тем, как файл был открыт.

+0

Я скопировал и выполнил ваш код, и я получил разные результаты от tellg() ... все еще выключен. И ваш второй пример фактически продемонстрировал проблему +1 для каждой новой строки, которую вы описали ранее. Таким образом, похоже, что просто использование двоичного режима - идеальное решение. – Michael

+0

@ Майкл I отправил отчет об ошибке в libstdC++ и опубликовал в списке рассылки, mingw-w64 также знает о проблеме сейчас, но неясно, будет ли оно исправлено или нет, и кем. – user657267

0

Спасибо за это, хотя это очень старый пост. Я больше застал эту проблему больше недели. Вот несколько примеров кода на моем сайте (версии 1 и 2 меню). Версия 1 использует представленное здесь решение, если кто-то хочет его увидеть.

:)

void customerOrder::deleteOrder(char* argv[]){ 
std::fstream newinFile,newoutFile; 
newinFile.rdbuf()->pubsetbuf(nullptr, 0); 
newinFile.open(argv[1],std::ios_base::in); 
if(!(newinFile.is_open())){ 
    throw "Could not open file to read customer order. "; 
} 
newoutFile.open("outfile.txt",std::ios_base::out); 
if(!(newoutFile.is_open())){ 
    throw "Could not open file to write customer order. "; 
} 
newoutFile.seekp(0,std::ios::beg); 
std::string line; 
int skiplinesCount = 2; 

if(beginOffset != 0){ 
    //write file from zero to beginoffset and from endoffset to eof If to delete is non-zero 
    //or write file from zero to beginoffset if to delete is non-zero and last record 
    newinFile.seekg (0,std::ios::beg); 
    // if primarykey < largestkey , it's a middle record 
    customerOrder order; 
    long tempOffset(0); 

    int largestKey = order.largestKey(argv); 
    if(primaryKey < largestKey) { 
     //stops right before "current..." next record. 
      while(tempOffset < beginOffset){ 
       std::getline(newinFile,line); 
     newoutFile << line << std::endl; 
       tempOffset = newinFile.tellg(); 
     } 
     newinFile.seekg(endOffset); 
     //skip two lines between records. 
      for(int i=0; i<skiplinesCount;++i) { 
      std::getline(newinFile,line); 
      } 
     while(std::getline(newinFile,line)) { 
      newoutFile << line << std::endl; 
     } 
    } else if (primaryKey == largestKey){ 
     //its the last record. 
     //write from zero to beginoffset. 
      while((tempOffset < beginOffset) && (std::getline(newinFile,line))) { 
       newoutFile << line << std::endl; 
       tempOffset = newinFile.tellg(); 
      } 
} else { 
     throw "Error in delete key" 
    } 
} else { 
//its the first record. 
//write file from endoffset to eof 
//works with endOffset - 4 (but why??) 
    newinFile.seekg (endOffset); 
    //skip two lines between records. 
    for(int i=0; i<skiplinesCount;++i) { 
     std::getline(newinFile,line); 
    } 
    while(std::getline(newinFile,line)) { 
     newoutFile << line << std::endl; 
    } 
} 
newoutFile.close(); 
newinFile.close(); 

}

beginOffset является специфической точкой в ​​файле (начало каждой записи), и endOffset конец записи, вычисленной в другой функции с tellg (findFoodOrder) Я не добавлял это, поскольку это может стать очень длинным, но вы можете найти на моем сайте (в разделе: меню версия 1 ссылка):

http://www.buildincode.com

+1

Пожалуйста, будьте осторожны с привязкой к собственному контенту на разных сайтах, вы не хотите быть [спамером] (http://stackoverflow.com/help/promotion). Вы должны включать в себя большинство контента здесь и использовать ссылку только в качестве ссылки. – NathanOliver

Смежные вопросы