if (len > n && strcmp (buf + len - n, extension) == 0)
fileName = CopyString(); // Создаем новую строку на основе данных буфера
else
fileName = strcat (strcpy (new char[len + n + 1], buf), extension);
}
return fileName;
}
Ранее мы рассматривали, как причесать неопрятную строку символов, то есть привести ее к каноническому виду, в котором отсутствуют начальные и конечные пробелы, а слова разделяются лишь одним пробелом. Создайте код функции Trim, которая осуществляет этот план.
void Trim (char* text) // Приводит строку символов к нормальному виду
{
bool was = true; // Флаг, указывающий, что предыдущий символ был пустой (пробел)
//====== Ваш код. Не забудьте завершить (нулем) новую каноническую строку
}
В бесконечном диалоге с пользователем важно освобождать память, занимаемую текущей структурой типа passwd. Разработайте код функции DeleteUser, которая освобождает поля структуры, а затем и саму структуру.
void DeleteUser (passwd *user)
{
//====== Ваш код
}
Функцию вывода данных о пользователе приведем полностью, так как она тривиальна.
void ShowUser (passwd* user)
{
if (user)
{
char line[] = "\n----------------------------------------------";
printf ("%s\n"
"User:\t %s\n"
"Pass:\t %s\n"
"UID:\t %d\n"
"GID:\t %d\n"
"Home:\t %s\n"
"Shell:\t %s\n"
"Comment: %s%s",
line, user->pw_name, user->pw_passwd, user->pw_uid,
user->pw_gid, user->pw_dir, user->pw_shell, user->pw_gecos, line);
}
}
Вернемся к одному из кирпичиков, который был использован в главной программе.
void OpenUserFile (char *fileName) // Пытаемся открыть файл и прочесть его заголовок
{
// Откройте файл c именем fileName и присвойте файловый указатель глобальной переменной fp
// Если не удалось, то выбросьте исключение типа "Couldn't open " + fileName
// Операцию "плюс" реализуйте с помощью функции strcat
ReadToken ("while reading file comment"); // Пытаемся прочесть заголовок файла (File Comment)
// Строка на входе: "while reading file
comment" будет выброшена в случае неудачи (конца файла)
printf ("\nOpenung file:\t%s\nFile comment:\t%s", fileName, buf);
ReadToken ("while reading file header"); // Пытаемся прочесть разделитель заголовка и списка
// Запомните файловую позицию начала списка в переменной posUsers
}
Функция ReadToken — одна из главных в функционировании приложения. Она читает из файла одну лексему. Важно помнить, что лексема ограничена либо разделителем (delim = ':'), либо символом конца строки ('\n').
void ReadToken (char* when) // Чтение очередной лексемы
{
int n = 0; // Счетчик символов
// Создайте цикл с логикой: Читаем, пока позволяет файл и буфер
{
char c = // Считываем один символ
// Если он не равен delim и не равен символу конца строки, то заносим его в буфер buf
// Иначе завершаем строку в буфере (нулем), вызываем Trim и уходим
}// Конец цикла
if (n == maxLen) // Если цикл завершился аварийно, то выбрасываем причину (строка when)
throw strcat (strcpy (buf, "ReadToken: Token is too long"), when);
else
throw strcat (strcpy (buf, "ReadToken: EOF "), when);
}
Удобно иметь еще одну функцию чтения лексемы из файла в буфер. Она создает и возвращает копию лексемы.
char* GetToken (char* when) // Читаем лексему и возвращаем ее копию
{
ReadToken (when);
return CopyString();
}
Следующий строительный кирпич нужен для работы с индексами (pw_uid и pw_gid).
short GetShort(char* when) // Читаем лексему, преобразуем ее в short и возвращаем
{
ReadToken (when);
return SetShort (buf);
}
Одна из самых сложных функций — это чтение файловых адресов: pw_dir и pw_shell. Сложность состоит в том, что в этой лексеме может присутствовать символ ':' (например, D:\Users\). Вспомните, что этот же символ является разделителем полей в нашей "базе данных". Надо как-то выявить файловый разделитель и не путать его с разделителем полей. Для этого пришлось разработать отдельную функцию GetPath.
char* GetPath() // Читает лексему, в которой может быть разделитель :
{
ReadToken ("while reading path drive"); // Читаем обычную лексему
if (strlen(buf) == 1)
{
char *disk = 0, *path = 0;
try
{
disk = strdup (buf); // Копируем лексему (имя диска)
ReadToken ("while reading path");
path = strdup (buf); // Копируем лексему (остальная часть файлового пути)
strcat (strcat (strcpy (buf, disk), ":"), path);
free (disk); free (path);
}
catch (char* s)
{
free (disk); free (path); // Особенности работы с функцией strdup (см. MSDN)
throw s;
}
}
return CopyString();
}
Разработаем еще один кирпич, запланированный в главной программе.
void QueryUser() // Диалог с пользователем
{
passwd* user = 0;
// Создайте бесконечный цикл (диалог с пользователем)
{
char *name = 0;
switch (Menu()) // Предложение альтернатив (искать запись по имени или по индексу)
{
case 'n': user = GetUserByName (GetString ("Enter a user name"));
break;
case 'i': user = GetUserById (SetShort(GetLine ("Enter a user id")));
break;
case 'q': throw "Stop"; // Нормальное завершение приложения
default: if (No ("Want to quit ? (Y/N)"))
continue;
}
// Выведите информацию о пользователе c адресом user
// Верните файловый указатель в начальную позицию
getch();
}
}
Следующая функция запланирована заказчиком. Мы изменили имя с учетом принятых норм (design guidelines).
passwd* GetUser() // Чтение информации о текущем пользователе
{
// Объявите и создайте новую структуру (user), описывающую пользователя
// Обнулите все ее поля
try
{
user->pw_name = GetToken ("name");
user->pw_passwd = GetToken ("while reading user pass");
user->pw_uid = GetShort ("while reading user ID");
user->pw_gid = GetShort ("while reading group ID");
user->pw_gecos = GetToken ("while reading user comment");
user->pw_dir = GetPath();
user->pw_shell = GetPath();
}
catch (char* s)
{
// Освободите память, занятую структурой, описывающей пользователя
if (strcmp (s, "ReadToken: EOF name") == 0) // Ожидаемое завершение приложения
return 0;
throw s; // Неожиданный конец файла
}
return user;
}
Эта функция также задана заказчиком.
passwd* GetUserByName (char *name) // Поиск строки, содержащей искомое имя name
{
printf ("\nLooking for:\t%s\n", name);
passwd *user;
// Создайте цикл с логикой: Читаем, пока есть пользователи
for (int i=1; (пока получаем пользователей) != 0; i++) // Пока не кончился файл (ожидаемо)
{
// Если имя текущего пользователя совпадает с искомым name
{
printf ("\n#%d. Found a user with the name = %s", i, name);
// Освободите память, занятую name
// Верните user
}
// Освободите память, занятую user
}
printf ("User: %s - not found\n", name);
return 0;
}
Логика поиска по индексу аналогична логике поиска по имени, поэтому создать код GetUserById несложно.
passwd* GetUserById (uid_t uid) // Поиск строки, содержащей искомый индекс uid
{
printf ("\nLooking for:\t%d\n", uid);
passwd *user;
// Создайте цикл с логикой: Читаем, пока есть пользователи
// и т.д.
printf ("User ID: %d - not found\n", uid);
return 0;
}
Соберите вместе все части приложения и отладьте его. Файл с данными вы можете взять в папке курса.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.