Как прочитать 6-й символ с конца файла - ifstream?

void maintainFileName ()
{
    std :: ifstream myfile;
    myfile.open ("zoomLevels.txt");

    if (myfile.is_open ())
    {
        // Move to end of the file, 
        myfile.seekg (0, std::ios::end);

        // and then six characters back to pick up the last file number.
        myfile.seekg (6, std::ios::beg);

        int len = 1;
        char *t = new char[len];

        myfile.read(t, len);
        qDebug () << "\nt: " << *t << "\n";
    }
    else
    {
        qDebug () << "\nsorry";
    }
}

Файл содержит это:

78.8115,29.582,1,01.rda
78.8115,29.582,2,02.rda
76.3671,30.2201,1,11.rda
76.3671,30.2201,2,12.rda
78.1908,30.3007,1,01.rda
78.1908,30.3007,2,02.rda
77.3284,29.1415,1,01.rda
77.3284,29.1415,2,02.rda
77.3064,29.1655,1,01.rda
77.3064,29.1655,2,02.rda

Эта функция возвращает значение 5, а шестой символ с конца — 0!
Где я ошибаюсь?


person Aquarius_Girl    schedule 05.09.2012    source источник
comment
Вы неправильно поняли значение параметра dir. Это не направление! Вы, вероятно, не первый, кого поймают: это совершенно неверно: это на самом деле позиция!   -  person J.N.    schedule 05.09.2012
comment
@Дж.Н. См. это: cplusplus.com/reference/iostream/istream/seekg там написано: Seeking direction. :)   -  person Aquarius_Girl    schedule 05.09.2012
comment
По ссылке: Направление поиска. Это объект типа ios_base::seekdir, указывающий абсолютную позицию, от которой применяется параметр смещения off. Направление/абсолютное положение. Неудивительно, что люди путаются.   -  person jrok    schedule 05.09.2012
comment
Однако следующая ссылка намного понятнее: en.cppreference.com/w/cpp/ io/basic_istream/seekg. Все еще имеет имя dir, вероятно, взятое из стандарта.   -  person J.N.    schedule 05.09.2012


Ответы (5)


Поиск произвольной позиции в текстовом файле является поведением undefined. На практике, возможно, он будет работать под различными Unix, но не где-либо еще. Если вы открываете файл в двоичном режиме, поиск разрешен. Формально, если открыть файл в бинарном режиме, в конце могут появиться лишние нулевые байты, но на практике сегодня это не проблема. Однако, если вы откроете его в двоичном режиме, вы можете увидеть что-то другое вместо '\n' в данных; под Windows, например, вы увидите последовательность из двух символов 0x0D, 0x0A.

Конечно, в вашем коде вы ищете с начала, а не с конца. Это также неопределенное поведение, но в большинстве случаев оно будет работать, пока вы ищете в первой строке.

И, наконец, шестой символ с конца в данных, которые вы показываете, — это '2', а не '0', как вы пишете. Но, конечно, в системах, отличных от Unix, вы можете легко увидеть что-то еще (или получить ошибку): возможно, '.' в Windows или или ошибку (или, может быть, '' ') в какой-нибудь операционной системе мейнфрейма.

person James Kanze    schedule 05.09.2012
comment
Ты был так прав. Я на Линуксе. Теперь я открыл файл в двоичном режиме, и был показан правильный результат. Конечно, первым символом в конце будет EOF, поэтому мой расчет был неверным. Спасибо за помощь. - person Aquarius_Girl; 05.09.2012
comment
@AnishaKaul: персонажа EOF нет. Должно быть, это одно из самых распространенных и неискоренимых заблуждений среди программистов на C. - person Kerrek SB; 05.09.2012
comment
Но когда я написал myfile.seekg (-7, std::ios::end);, он показал 0. Но, если вы действительно считаете значения, 0 находится на шестой позиции с конца! - person Aquarius_Girl; 05.09.2012
comment
В конце может быть '\n'. - person jrok; 05.09.2012
comment
@jrok АГА, абсолютно, ЕСТЬ. :) - person Aquarius_Girl; 05.09.2012
comment
@AnishaKaul Если вы работаете в Linux, этот тип позиционирования действительно работает, даже в текстовом режиме (по крайней мере, с библиотекой, поставляемой с g++). Во многих смыслах текстовый режим предназначен для обозначения того, что делает Unix. Но, как я уже сказал, вы просчитались. В файле нет символа EOF, но каждая строка заканчивается знаком '\n', который присутствует в файле. (В двоичном режиме под Windows вам придется считать два байта для конца строки. На мэйнфрейме вы вообще не сможете искать относительно конца строки. неопределенное поведение.) - person James Kanze; 05.09.2012
comment
Прочитав разделы стандарта, связанные с потоками, я не смог найти ни одного утверждения, ограничивающего поиск абсолютной позиции двоичными потоками. Я что-то упустил из виду, или стандарт действительно определяет это правильно, но на некоторых платформах слишком сложно реализовать это правильно? - person jogojapan; 05.09.2012
comment
Для seekpos (§27.9.1.5/17 в C++11), если sp не был получен предыдущим успешным вызовом одной из функций позиционирования (seekoff или seekpos) в том же файле, эффекты не определены. Для seekoff , C++ перенаправляет на fseek в стандарте C, который имеет аналогичные ограничения (хотя у меня нет копии здесь, чтобы процитировать); все, что допустимо для потока, открытого в текстовом режиме, это 0 или значение, возвращаемое из ftell, а whence должно быть SEEK_SET. - person James Kanze; 05.09.2012

myfile.seekg (6, std::ios::beg);

здесь вы перемещаете 6 символов от начала, а не к началу. Просто используйте

myfile.seekg (-6, std::ios::end);
person SingerOfTheFall    schedule 05.09.2012
comment
-6 теперь показывает 2 в результате, тогда как результат должен быть 0. :( - person Aquarius_Girl; 05.09.2012
comment
Это все еще неопределенное поведение. - person James Kanze; 05.09.2012

Первый поиск переходит в конец, а второй — в начало + 6.

Использовать:

 myfile.seekg(-6, std::ios::end);
person rasmus    schedule 05.09.2012

Вы можете попробовать определить полный размер файла с помощью tellg() в конце файла и вычесть ваши числа, проверить его на> 0, а затем снова найти его. Если вы попробуете это, вы также должны убедиться, что файл открыт в двоичном режиме (помню, может быть недостаток)

myfile.seekg (0, ios::end);
// You have to ensure, myfile.tellg() > 6
myfile.seekg ( myfile.tellg() - 6, ios:beg );

Редактировать:

seekg принимает тип std::streamoff в качестве смещения.

Стандарт (ISO/IEC 14882:2003) говорит очень интересные вещи об этой «проблеме», о которой многие говорят.

В разделе 27.2. Форвардные объявления, streampos относится к классу fpos.

Если пойти дальше, мы можем найти таблицу требований для fpos в разделе 27.4.3.2, где мы можем получить замыкание на тип streamoff, и здесь явным требованием является: q = p + o, поэтому класс fpos ДОЛЖЕН ОПРЕДЕЛЯТЬ оператор+(смещение). Поскольку объект fpos также должен определять O(p) с возвращаемым типом OFF_T, который является внутренним типом, но есть также оператор, std::streamoff имеет тип OFF_T, у нас есть замкнутый цикл для определения внутри стандартного для этой операции.

Таким образом, эта операция должна быть четко определена.

Другие мнения приветствуются.

person Sven    schedule 05.09.2012
comment
Неопределенное поведение. И стандарт очень неоднозначен в отношении того, что может означать добавление или вычитание значения из streampos. (Стандарт на самом деле предъявляет невыполнимые требования к поведению streampos.) - person James Kanze; 05.09.2012
comment
Я отредактировал, почему это четко определено в стандарте, другие мнения приветствуются для обсуждения. - person Sven; 05.09.2012
comment
Стандарт говорит, что явный поиск в текстовом файле, отличном от позиции, возвращаемой tell, является неопределенным поведением. Вам не нужно рассуждать об этом. И, конечно же, на практике арифметика на streampos не работает с файлами, открытыми в текстовом режиме, кроме как в Unix. - person James Kanze; 05.09.2012
comment
Не могли бы вы добавить сюда ссылку? И если вы читали пост выше, я включил ограничение на бинарные открытые файлы. - person Sven; 05.09.2012
comment
Ага, тогда вы изменили задачу. Файл ОП был открыт в текстовом режиме. (Но я все еще не слишком уверен, что может означать добавление или вычитание int из streampos. streampos содержит информацию о состоянии, а также позицию. Подумайте, что это может означать для широкого потока символов, наполненного UTF-8 локаль, например. - person James Kanze; 05.09.2012

Сначала перейдите в конец файла: is.seekg (0, ios::end);, затем сохраните позицию: file_end_position = is.tellg();. Теперь перейдите к этой позиции: is.seekg (file_end_position-6, ios::end); и прочитайте 6-й символ с конца: is.read (buf,1);

#include <iostream>
#include <fstream>
using namespace std;

int main () {
  int file_end_position;

  ifstream is;
  is.open ("test.txt", ios::binary );

  // get size of file:
  is.seekg (0, ios::end);
  file_end_position = is.tellg();
  is.seekg (0, ios::beg);


  //go to the end  of the file -6
  is.seekg (file_end_position-6, ios::end);

  char buf[1];

  // read the 6th character from the end of the file  
  is.read (buf,1);
  is.close();



  delete[] buffer;
  return 0;
}
person Software_Designer    schedule 05.09.2012