© Иван Рощин, Москва
| ZXNet | : 500:95/462.53 |
| : bestview@mtu-net.ru | |
| WWW | : http://www.ivr.da.ru |
(«Радиомир. Ваш
компьютер» 11/2004)
Дополненная версия.
При выкладывании на своей
![]() |
![]() |
Но то, что удобно для посетителей страницы, не так удобно для её
создателя. :) Каждый раз при добавлении нового архива или при
изменении уже существующего архива мне приходилось вычислять его размер в
килобайтах и добавлять эти сведения в
На жёстком диске моего компьютера находится локальная копия
В
Случаю, когда размер архива указан в тексте ссылки, соответствует примерно
такой фрагмент
<p class="ms"><a href="all_zx.zip">Скачать все программы для ZX (292 КБ ZIP)</a></p>
Случаю, когда размер архива указан в атрибуте «title», соответствует примерно такой фрагмент (этот фрагмент взят как раз из документа, часть которого изображена на рис. 2):
<p><b>2.03.2004.</b> Выложена <a href="prog/bv_2_19.zip" title="Скачать (75 КБ ZIP)">очередная версия BestView — 2.19</a>.</p>
Случаю, когда я добавляю новую ссылку на
В моих
От программы требуется, чтобы она просматривала указанный
Исходя из сказанного выше, можно предложить такой алгоритм: просматриваем
обрабатываемый HTML-файл от начала к концу; если встретили строку
«href="» (или «HREF="» регистр
букв не имеет значения) значит, далее (до следующего символа
«"») следует адрес ссылки (пока неизвестно,
«нужная» это ссылка или нет), и этот адрес надо запомнить. Если
встретили строку вида «(* КБ ZIP)», где
«*» ни одной, одна или несколько десятичных
цифр, значит, последняя встретившаяся ссылка (её адрес
запомнен) «нужная». Преобразуем адрес этой ссылки в
полное имя соответствующего файла
Замечу, что в программе используется несколько модифицированный, более
лёгкий для реализации вариант вышеописанного алгоритма: вместо
непосредственного изменения исходного
Ещё одна особенность реализации: при описании алгоритма упоминается о
поиске в файле строки вида «(* КБ ZIP)», но так
как эта строка содержит русские буквы, возникает сложность: в различных
кодировках коды русских букв различны, а кодировка обрабатываемого
/* =============================
File: "add_kb.c"
Compiler: Turbo C 2.0
============================= */
#include <io.h>
#include <stdio.h>
#include <dir.h>
#include <ctype.h>
#include <string.h>
/* ========================== Описание функций =========================== */
int cmp_str (FILE* f, char* s);
long def_len (char* href, char* cur_path);
void chdir_1 (char* path);
FILE* fopen_1 (char* name, char* mode);
int fgetc_1 (FILE* f);
void ungetc_1 (int c, FILE* f);
void fputc_1 (int c, FILE* f);
void fputs_1 (char* s, FILE* f);
void fclose_1 (FILE* f);
/* ======================== Пошла сама программа ========================= */
#define MAX_HREF_LEN 255 /* Максимальная длина адреса ссылки. */
#define MAX_PATH_LEN 128 /* Максимальная длина пути к файлу. */
int code_k, code_b; /* Коды русских букв "К" и "Б" в кодировке
обрабатываемого HTML-файла (определяются
автоматически). */
int main (int argc, char *argv[])
{
FILE *f_src; /* Исходный файл. */
FILE *f_dst; /* Формируемый файл. */
int href_found=0; /* Признак "в исходном файле уже обнаружена
хотя бы одна ссылка". */
char href[MAX_HREF_LEN+1]; /* Адрес (т.е. значение атрибута "href")
последней встреченной в исходном файле
ссылки. */
int href_overflow; /* Признак "длина адреса последней встреченной
в исходном файле ссылки оказалась больше
максимальной" (т.е. больше MAX_HREF_LEN). */
long l,l_kb; /* Длина zip-архива, на который указывает ссылка
(l - длина в байтах, l_kb - в килобайтах). */
char cur_path[MAX_PATH_LEN+1]; /* Путь к обрабатываемому HTML-файлу. */
char *temp_name="$.tmp"; /* Имя формируемого временного файла. */
int i,c;
/* ============ Подготовительные действия ============ */
printf ("\n+------------------------------------------------------------------------+\n");
printf ("| Add_KB - автоматическое проставление размеров файлов в HTML-документах.|\n");
printf ("| (c) Иван Рощин, Москва, 22.04.2004 |\n");
printf ("| E-mail: bestview@mtu-net.ru WWW: http://www.ivr.da.ru |\n");
printf ("+------------------------------------------------------------------------+\n\n");
if (argc!=2) {printf ("Syntax: add_kb.exe filename.htm\n\n"); exit(1);}
if (getcwd(cur_path,MAX_PATH_LEN)==NULL)
{printf ("Ошибка при определении пути к HTML-файлу!\n\n"); exit (1);}
f_src=fopen_1(argv[1],"rb");
f_dst=fopen_1(temp_name,"wb");
/* ================== Главный цикл =================== */
main_loop:
/* Если в исходном файле с текущей позиции находится строка 'href="', то
считаем, что обнаружена ссылка. Устанавливаем признак href_found.
Считываем идущий за 'href="' адрес ссылки, пока не встретим символ '"',
и помещаем этот адрес в переменной href. При этом копируем в выходной
(временный) файл символы, считанные из исходного файла. Если окажется,
что адрес ссылки длиннее MAX_HREF_LEN, то устанавливаем признак
href_overflow (нельзя сразу же прерывать выполнение программы с выдачей
сообщения о слишком длинном адресе ссылки, так как эта ссылка вполне
может и не относиться к файлу, размер которого требуется указывать). */
if (cmp_str(f_src,"href=\"")==1)
{
href_found=1;
i=0; /* Текущая длина адреса ссылки. */
href_overflow=0;
fputs_1 ("href=\"",f_dst);
while ((c=fgetc_1(f_src))!='"')
{
if (c==EOF) goto end_loop; /* Если конец файла - выходим из главного цикла. */
if (i<MAX_HREF_LEN) {href[i]=c; href[i+1]=0; i++;} else href_overflow=1;
fputc_1(c,f_dst);
}
fputc_1('"',f_dst);
goto main_loop; /* Возвращаемся к началу главного цикла. */
}
/* Если в исходном файле с текущей позиции находится строка вида
"(* ? ZIP)" (о том, что в этой строке обозначают символы "*"
и "?", можно узнать из описания функции cmp_str), значит, надо определить
длину файла (zip-архива), соответствующего последней встреченной ссылке,
и подставить её в формируемый файл вместо "*". */
if (cmp_str(f_src,"(* ? ZIP)")==1)
{
if (href_found==0)
{
printf ("Ошибка: в исходном файле встречена строка вида \"(* КБ ZIP)\",\n");
printf ("но нет ссылки, предшествующей этой строке!\n\n");
exit (1);
}
printf ("%s\n",href); /* Печатаем на экране адрес ссылки. */
if (href_overflow==1)
{
printf ("Ошибка: в исходном файле встречена строка вида \"(* КБ ZIP)\",\n");
printf ("но адрес ссылки, предшествующей этой строке, оказался слишком длинным!\n\n");
exit (1);
}
l=def_len(href,cur_path); /* Узнали длину файла, соответствующего ссылке. */
/* Теперь преобразуем полученную длину файла в байтах в длину в килобайтах,
округляя до ближайшего целого числа. Но если длина файла - от 1 до 511
байтов включительно, то устанавливаем длину в килобайтах равной 1, а не
0, так как "0 КБ" будет выглядеть несколько странно: всё-таки длина файла
не равна 0. */
if ((l>=1)&&(l<=511)) l_kb=1;
else l_kb=(l+512)/1024;
fputc_1 ('(',f_dst);
if (fprintf(f_dst,"%ld",l_kb)==EOF) {printf ("\nОшибка записи!\n\n"); exit (1);}
fputs_1 (" ",f_dst);
fputc_1 (code_k,f_dst);
fputc_1 (code_b,f_dst);
fputs_1 (" ZIP)",f_dst);
goto main_loop; /* Возвращаемся к началу главного цикла. */
}
/* Если в исходном файле с текущей позиции нет ни строки 'href="', ни строки
вида "(* ? ZIP)", то, если ещё не достигнут конец исходного
файла, просто копируем символ из исходного файла в формируемый временный
файл и переходим к началу главного цикла. */
c=fgetc_1(f_src);
if (c!=EOF) {fputc_1(c,f_dst); goto main_loop;}
/* ======= Выполнение главного цикла закончено ======= */
end_loop:
fclose_1 (f_src);
fclose_1 (f_dst);
/* Исходный файл успешно обработан. Теперь открываем сформированный
временный файл для чтения, а исходный файл - для записи, и копируем
содержимое временного файла в исходный. Затем удаляем временный файл. */
chdir_1 (cur_path);
f_src=fopen_1(temp_name,"rb");
f_dst=fopen_1(argv[1],"wb");
while((c=fgetc(f_src))!=EOF) fputc (c,f_dst);
fclose_1 (f_src);
fclose_1 (f_dst);
unlink (temp_name);
printf ("\nAll OK!\n\n");
exit (0);
}
/* ==========================================================================
Функция def_len - определение длины файла по адресу ссылки на этот файл.
Вход: href - адрес ссылки на файл,
cur_path - путь к обрабатываемому HTML-файлу.
Выход: длина (в байтах) файла, на который указывает ссылка.
========================================================================= */
long def_len (char* href, char* cur_path)
{
char temp_path[MAX_PATH_LEN+1]; /* Здесь будем формировать путь к файлу,
на который указывает ссылка. */
int i;
long l;
FILE *f_tmp;
strcpy (temp_path,cur_path); /* Первоначальное значение пути - это путь
к обрабатываемому HTML-файлу. */
/* Для более удобного выполнения последующих действий приводим путь к виду
без '\' в конце. Если путь указывает не на корневой каталог (тогда его
длина больше трёх символов), то это условие уже выполняется, иначе (путь
вида "C:\", длина - три символа) отбрасываем последний символ пути. */
if (strlen(temp_path)==3) temp_path[2]=0;
while (strchr(href,'/')!=NULL) /* Пока в адресе ссылки ещё есть "/". */
{
/* Если первые три символа href - "../", то переходим на уровень вверх:
отрезаем эти три символа от href и отрезаем от temp_path все символы,
начиная с последнего "\" и до конца. */
if (memcmp(href,"../",3)==0)
{
href=href+3;
if (strlen(temp_path)==2) /* Если уже в корневом каталоге. */
{
printf ("Ошибка: в обрабатываемой ссылке слишком много переходов на уровень вверх!\n\n");
exit (1);
}
*strrchr(temp_path,'\\')=0;
continue;
}
/* Иначе начало href до первого "/" - это имя каталога, в который надо
перейти. Отрезаем это имя от href и добавляем к temp_path. */
i=strchr(href,'/')-href; /* Длина имени каталога. */
if ((strlen(temp_path)+1+i)>MAX_PATH_LEN)
{printf ("Ошибка: слишком длинный путь к файлу!\n\n"); exit (1);}
strncat(temp_path,"\\",1);
strncat(temp_path,href,i);
href=href+1+i;
}
/* Теперь в href осталось только имя файла, а путь к файлу сформирован
в temp_path. Если этот путь указывает на корневой каталог (тогда его
длина - два символа), добавляем "\" в конце. */
if (strlen(temp_path)==2) {temp_path[2]='\\'; temp_path[3]=0;}
/* Осталось определить длину этого файла. */
chdir_1 (temp_path);
f_tmp=fopen_1(href,"rb");
l=filelength(fileno(f_tmp));
fclose_1(f_tmp);
return (l);
}
/* ==========================================================================
Функция cmp_str - проверка совпадения (без учёта регистра букв) строки s
и того, что находится в файле f с текущей позиции. Символу "*" в строке
соответствуют ни одной, одна или несколько десятичных цифр в файле.
Символу "?" в строке соответствуют любые два символа в файле.
При совпадении функция возвращает 1, а позиция в файле указывает на первый
символ после строки; если в строке был символ "?", то коды соответствующих
ему двух символов в файле сохраняются в глобальных переменных code_k и
code_b.
При несовпадении функция возвращает 0, позиция в файле не меняется.
========================================================================= */
int cmp_str (FILE* f, char* s)
{
fpos_t pos;
int c,i;
int k,b;
int result=1;
fgetpos (f,&pos); /* Запомнили текущую позицию в файле. */
for (i=0;i<strlen(s);i++)
{
if (s[i]=='*')
{
while (isdigit(c=fgetc_1(f))); /* Пропускаем все цифры. */
if (c!=EOF) ungetc_1(c,f);
continue;
}
if (s[i]=='?')
{
k=fgetc_1(f); if (k==EOF) {result=0; break;}
b=fgetc_1(f); if (b==EOF) {result=0; break;}
continue;
}
if (tolower(fgetc_1(f))!=tolower(s[i])) {result=0; break;}
}
if (result==0) fsetpos (f,&pos); /* Восстановили позицию в файле. */
else {code_k=k; code_b=b;}
return (result);
}
/* ==========================================================================
Аналоги некоторых функций для работы с файлами (chdir, fgetc, ungetc,
fputc, fputs, fopen, fclose). При ошибке прерывают выполнение программы,
печатая соответствующее сообщение; таким образом, после вызова этих функций
не надо проверять, была ли ошибка.
========================================================================= */
void chdir_1 (char* path)
{
if (chdir(path)!=0)
{printf ("Ошибка при переходе в каталог \"%s\"!\n\n",path); exit (1);}
}
int fgetc_1 (FILE* f)
{
int c;
c=fgetc(f); if ferror(f) {printf ("\nОшибка чтения!\n\n"); exit (1);}
return (c);
}
void ungetc_1 (int c, FILE* f)
{
if (ungetc(c,f)==EOF) {printf ("\nОшибка при вызове функции \"ungetc\"!\n\n"); exit (1);}
}
void fputc_1 (int c, FILE* f)
{
if (fputc(c,f)==EOF) {printf ("\nОшибка записи!\n\n"); exit (1);}
}
void fputs_1 (char* s, FILE* f)
{
if (fputs(s,f)==EOF) {printf ("\nОшибка записи!\n\n"); exit (1);}
}
FILE* fopen_1 (char* name, char* mode)
{
FILE* f;
char* s;
char* s1="чтение";
char* s2="запись";
f=fopen(name,mode);
if (f==NULL)
{
if (mode[0]=='r') s=s1; else s=s2;
printf ("\nОшибка открытия файла \"%s\" на %s!\n\n",name,s);
exit (1);
}
return (f);
}
void fclose_1 (FILE* f)
{
if (fclose(f)==EOF) {printf ("\nОшибка закрытия файла!\n\n"); exit (1);}
}
Использовать программу, конечно, имеет смысл только в случае, если у вас такая же ситуация, как описано в разделе «Постановка задачи». Также обратите внимание на следующие ограничения программы:
При запуске программы необходимо указать в командной строке имя
обрабатываемого
add_kb.exe filename.htm
В процессе работы программа выводит адреса встретившихся в этом файле «нужных» ссылок. После успешной обработки файла программа печатает сообщение «All OK!» и заканчивает работу.
Как уже упоминалось в разделе «Описание
алгоритма», в процессе обработки
После того, как исходный
Программа возвращает операционной системе код завершения: 0 в случае
удачного завершения и 1 при
Кстати, помимо выполнения основной задачи, ещё одна польза от программы в
том, что она обнаруживает ситуацию, когда в «нужной» ссылке адрес
На моей
Раньше ей соответствовал следующий фрагмент
<a href="txt_zip/all_txt.zip">Скачать <nobr>txt-версии</nobr> всех статей (1370 КБ ZIP)</a>
Но в один далеко не прекрасный день суммарный объём файлов на моей
Пришлось изменить соответствующий фрагмент
<a href="http://www.comail.ru/~ivr/all_txt.zip">Скачать <nobr>txt-версии</nobr> всех статей (1370 КБ ZIP)</a>
Таким образом, проблема с превышением
Мне, тем не менее, хотелось
Смотрите: хотя в Интернете файл «all_txt.zip» и расположен
теперь по другому адресу, но в моей локальной копии
<a href="http://www.comail.ru/~ivr/all_txt.zip"><!-- href="txt_zip/all_txt.zip"-->Скачать <nobr>txt-версии</nobr> всех статей (1370 КБ ZIP)</a>
Теперь программа при обработке этого фрагмента, встретив строку
«(1370 КБ ZIP)», будет считать адресом
последней встреченной ссылки именно «txt_zip/all_txt.zip»
(напомню, адресом последней ссылки программа считает то, что было после
последнего встреченного «href="») и, таким
образом, сможет определить длину файла «all_txt.zip» и проставить
её в обрабатываемом
|
Другие мои статьи о создании и/или обработке
|