C++. Сборник рецептов - Д. Стефенс
Шрифт:
Интервал:
Закладка:
Пример 10.7. Чтение данных из потока в объект
#include <iostream>
#include <istream>
#include <fstream>
#include <string>
using namespace std;
class Employee {
friend ostream& operator<< // Они должны быть друзьями,
(ostream& out, const Employee& emp); // чтобы получить доступ к
friend istream& operator>> // неоткрытым членам
(istream& in, Employee& emp);
public:
Employee() {}
~Employee() {}
void setFirstName(const string& name) {firstName_ = name;}
void setLastName(const string& name) {lastName_ = name;}
private:
string firstName_;
string lastName_;
};
// Передать в поток объект Employee...
ostream& operator<<(ostream& out, const Employee& emp) {
out << emp.firstName_ << endl;
out << emp.lastName_ << endl;
return(out);
}
// Считать из потока объект Employee
istream& operator>>(istream& in, Employee& emp) {
in >> emp.firstName_;
in >> emp.lastName_;
return(in);
}
int main() {
Employee emp;
string first = "William";
string last = "Shatner";
emp.setFirstName(first);
emp.setLastName(last);
ofstream out("tmp\emp.txt");
if (!out) {
cerr << "Unable to open output file.n";
exit(EXIT_FAILURE);
}
out << emp; // Записать Emp в файл
out.close();
ifstream in("tmp\emp.txt");
if (!in) {
cerr << "Unable to open input file.n";
exit(EXIT_FAILURE);
}
Employee emp2;
in >> emp2; // Считать файл в пустой объект
in.close();
cout << emp2;
}
ОбсуждениеПри создании класса, считываемого из потока, выполняемые этапы почта совпадают с этапами записи объекта в поток (только они имеют обратный характер) Если вы еще не прочитали рецепт 10.4, это необходимо сделать сейчас, чтобы был понятен пример 10.7.
Во-первых, вам необходимо объявить operator>> как дружественный для вашего целевого класса, однако в данном случае вам нужно, чтобы он использовал поток istream, а не ostream. Затем определите оператор operator>> (вместо operator<<) для прямого чтения значений из потока в каждую переменную-член вашего класса. Завершив чтение данных, возвратите входной поток.
Смотри такжеРецепт 10.4.
10.6. Получение информации о файле
ПроблемаТребуется получить информацию о файле, например о его размере, номере устройства, времени последнего изменения и т.п.
РешениеИспользуйте вызов системной C-функции stat, определенной в <sys/stat.h>. См. Пример 10.8, где показано типичное применение stat для вывода на печать некоторых атрибутов файла.
Пример 10.8. Получение информации о файле
#include <iostream>
#include <ctime>
#include <sys/types.h>
#include <sys/stat.h>
#include <cerrno>
#include <cstring>
int main(int argc, char** argv) {
struct stat fileInfo;
if (argc < 2) {
std::cout << "Usage: fileinfo <file name>n";
return(EXIT_FAILURE);
}
if (stat(argv[1], &fileInfo) != 0) { // Используйте stat() для получения
// информации
std::cerr << "Error: " << strerror(errno) << 'n';
return(EXIT_FAILURE);
}
std::cout << "Type::";
if ((fileInfo.st_mode & S_IFMT) == S_IFDIR) { // Из sys/types.h
std::cout << "Directoryn";
} else {
std::cout << "Filen";
}
std::cout << "Size : " <<
fileInfo.st_size << 'n'; // Размер в байтах
std::cout << "Device : " <<
(char)(fileInfo.st_dev + 'A') >> 'n'; // Номер устройства
std::cout << "Created : " <<
std::ctime(&fileInfo.st_ctime); // Время создания
std::cout << "Modified : " <<
std.:ctime(&fileInfo.st_mtime); // Время последней модификации
}
ОбсуждениеСтандартная библиотека C++ обеспечивает операции с содержимым файловых потоков, но она не имеет встроенных средств по чтению и изменению поддерживаемых ОС метаданных файла, например размера файла, его владельца, прав доступа, различных времен и другой информации. Однако стандартный С содержит несколько стандартных библиотек системных вызовов, которые можно использовать для получения различной информации о файле, что сделано в примере 10.8.
Существует два средства, обеспечивающие получение информации о файле. Во-первых, это структура struct с именем stat, которая содержит члены с информацией о файле, и, во-вторых, системный вызов (функция) с тем же самым именем, который обеспечивает получение любой запрошенной информации о файле, помещая ее в структуру stat. Системный вызов — это функция, обеспечивающая некоторую системную службу ОС. Ряд системных вызовов является частью стандартного С, и многие из них стандартизованы и входят в состав различных версий Unix. Структура stat имеет следующий вид (из книги Кернигана (Kernigan) и Ричи (Richie) «The С Programming Language», [издательство «Prentice Hall»]).
struct stat {
dev_t st_dev; /* устройство */
ino_t st_ino; /* номер inode */
short st_mode; /* вид */
short st_nlink /* число ссылок на файл */
short st_uid; /* пользовательский идентификатор владельца */
short st_gid; /* групповой идентификатор владельца */
dev_t st_rdev; /* для особых файлов */
off_t st_size; /* размер файла в символах */
time_t st_atime; /* время последнего доступа */
time_t st_mtime; /* время последней модификации */
time_t st_ctime; /* время последнего изменения inode */
};
Смысл каждого члена stat зависит от ОС. Например, st_uid и st_gid не используются в системах Windows, в то время как в системах Unix они фактически содержат идентификаторы пользователя и группы владельца файла. Воспользуйтесь документацией ОС, чтобы узнать, какие значения поддерживаются и как они интерпретируются.
В примере 10.8 показано, как можно отображать на экране некоторые переносимые члены stat. st_mode содержит битовую маску, описывающую тип файла. Она позволяет узнать, является ли файл каталогом или нет. st_ size задает размер файла в байтах. Три члена типа size_t определяют время последнего доступа, модификации и создания файлов.
Остальные члены содержат информацию, зависящую от операционной системы. Рассмотрим st_dev: в системах Windows этот член содержит номер устройства (дисковода) в виде смещения от буквы А, представленной в коде ASCII (именно поэтому в примере я добавляю 'A', чтобы получить буквенное обозначение дисковода). Но в системе Unix это будет означать нечто другое; значение этого члена передайте в системный вызов ustat, и вы получите имя файловой системы.
Если вам требуется получить дополнительную информацию о файле, лучше всего обратиться к документации вашей ОС. Стандартные системные вызовы C-функций ориентированы на Unix, поэтому они обычно приносят больше пользы в системах Unix (и совместно с ними может использоваться ряд других системных вызовов). Если вы не используете Unix, вполне возможно, что в вашей ОС имеются поставляемые со средой разработки собственные библиотеки, которые позволяют получать более детальную информацию.
10.7. Копирование файла
ПроблемаТребуется скопировать файл, причем так, чтобы эта операция была переносимой, т.е. без использования зависящего от ОС программного интерфейса.
РешениеИспользуйте файловые потоки С++, определенные в <fstream>, для копирования одного потока в другой. Пример 10.9 показывает, как можно скопировать поток с помощью буфера
Пример 10.9. Копирование файла
#include <iostream>
#include <fstream>
const static int BUF_SIZE = 4096;
using std::ios_base;
int main(int argc, char** argv) {
std::ifstream in(argv[1],
ios_base::in | ios_base::binary); // Задается двоичный режим, чтобы
std::ofstream out(argv[2], // можно было обрабатывать файлы с
ios_base::out | ios_base::binary), // любым содержимым
// Убедитесь, что потоки открылись нормально...
char buf[BUF_SIZE];
do {
in.read(&buf[0], BUF_SIZE); // Считать максимум n байт в буфер,
out.write(&buf[0], in.gcount()); // затем записать содержимое буфера
} while (in.gcount() > 0); // в поток вывода.