Страница Ивана Рощина > Статьи >

© Иван Рощин, Москва

ZXNet: 500:95/462.53
E-mail: bestview@mtu-net.ru
WWW: http://www.ivr.da.ru

Программа для разрезания файлов

Радиомир. Ваш компьютер» 5/2004)
Исправленная и дополненная версия.
Дата последнего редактирования: 2.11.2004.

Введение
Руководство пользователя
Исходный текст программы на Turbo C
Дополнительная информация
Предупреждение

Введение

Предлагаю вашему вниманию программу, с помощью которой можно разрезать заданный файл на части указанной длины, или получить часть файла указанной длины, начиная с указанного смещения, или пропустить первые X байтов файла, а остаток разрезать на части размером Y байтов, или разделить файл на части в X, Y, Z байтов и остаток… Как вы увидите дальше, программа позволяет разрезать файл на части и более сложным образом.

(Замечу, что на самом деле «разрезание» — не совсем точный термин, так как программа не изменяет исходный файл, а создаёт новые файлы — части исходного.)

Руководство пользователя

При запуске программы надо указать в командной строке имя исходного файла, имя получаемых файлов (без расширения — оно будет сформировано автоматически) и команды, определяющие, как именно файл будет разбит на части.

Пример:

file_cut.exe 1.txt aaa 0x735-,92163,*

Исходный файл предполагается находящимся в текущем каталоге, там же будут сформированы и выходные файлы. Расширение первого сформированного файла будет «001», второго — «002», и так далее (если получится более 999 частей, будут записаны только первые 999).

Теперь о командах, определяющих, как именно файл будет разбит на части. Может быть указана одна или несколько команд; если их несколько, они разделяются запятыми. Всего можно использовать четыре различных типа команд:

n записать часть длиной n байтов, начиная с текущей позиции в исходном файле (если до конца исходного файла осталось менее n байтов, размер записанной части будет равен оставшейся части файла);
n– пропустить n байтов с текущей позиции в исходном файле (если до конца исходного файла осталось менее n байтов, будут пропущены все оставшиеся байты);
n* записывать части длиной n байтов, начиная с текущей позиции в исходном файле, пока не будет достигнут конец файла (таким образом, длина последней записанной части может оказаться меньше n);
* записать часть, начиная с текущей позиции в исходном файле и до конца файла. Эта команда, очевидно, может быть только последней.

Во всех командах n — число от 1 до 0x7FFFFFFF (2147483647), записанное в десятичном или в шестнадцатеричном виде (при записи в шестнадцатеричном виде число должно предваряться символами «0x», регистр символов в записи числа не имеет значения).

Программа может обработать не более 64 команд (определяется значением константы MAX_COMM в исходном тексте программы), но столько команд вы всё равно не введёте из-за ограничения на длину командной строки в DOS.

Команды выполняются в порядке их записи. Перед выполнением первой команды текущая позиция в исходном файле — это начало файла. Работа программы заканчивается, если все команды выполнены, или если достигнут конец исходного файла, или если уже записано 999 частей файла, или если произошла какая-нибудь ошибка при работе с файлами.

Всего лишь четыре типа команд дают возможность достаточно разнообразной обработки файлов. Ниже приведены несколько примеров.

  1. Получить первые 500 байтов файла.
    500
    
  2. Наоборот, пропустить первые 500 байтов и получить остаток файла.
    500-,*
    
  3. Получить часть файла, начинающуюся со смещения 0x1000 от начала и длиной 0x300 байтов.
    0x1000-,0x300
    
  4. Разбить файл на четыре части: 100, 200, 300 байтов и остаток.
    100,200,300,*
    
  5. Разбить файл на части по 10000 байтов (кроме, может быть, последней части).
    10000*
    
  6. Пропустить 0x400 байтов файла, записать часть длиной 123 байта, потом пропустить ещё 0x100 байтов и записать остаток.
    0x400-,123,0x100-,*
    

Также замечу, что не очень сложно доработать программу, добавив ещё какие-нибудь типы команд.

Исходный текст программы на Turbo C

/* =============================
     File: "file_cut.c"
     Compiler: Turbo C 2.0
 ============================= */

#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <limits.h>

void save_part (char *nameonly, long begin_part, long len_part);
int work_com (char* s);
int get_val (char **s, long *v);

#define MAX_COMM 64        /* Максимальное количество команд. */

int n_comm=0;              /* Количество команд. */
int comm_type [MAX_COMM];  /* Тип команды (0-3). */
long comm_val [MAX_COMM];  /* Число в команде (если есть). */
int n_part=1;              /* Номер записываемой части. */
long len_src, point_src=0; /* Длина исходного файла и положение указателя. */
FILE *f_src, *f_dst;

void main (int argc, char *argv[])
{
 printf ("\nFile_Cut by Ivan Roshin, 28.03.2004.\n");
 if (argc!=4)
 {printf ("Syntax: file_cut.exe source-file destiny-file(without extension) commands\n"); exit(0);}
 /* Разбираем заданные команды. */
 if (work_com(argv[3])==1) {printf ("Incorrect commands!\n"); exit(0);}
 /* Проверяем, чтобы имя формируемых частей было не длиннее 8 символов. */
 if (strlen(argv[2])>8) {printf ("Error in destiny filename!\n"); exit(0);}
 f_src=fopen(argv[1],"rb"); /* Открываем исходный файл. */
 if (f_src==NULL) {printf ("Can't open source file!\n"); exit(0);}
 len_src=filelength(fileno(f_src)); /* Определяем длину исходного файла. */
 if (len_src==-1) {printf ("Can't define source length!\n"); exit(0);}

 /* Выполнение указанных команд (если длина исходного файла больше 0). */
 if (len_src>0)
 {
  int i;
  for (i=0; i<n_comm; i++)
  {
   switch (comm_type[i])
   {
    case 0: /* Запись части файла заданной длины. */
     if (comm_val[i]>(len_src-point_src)) comm_val[i]=len_src-point_src;
     save_part (argv[2], point_src, comm_val[i]);
     point_src+=comm_val[i]; n_part++;
     break;
    case 1: /* Пропуск части файла заданной длины. */
     if (comm_val[i]>(len_src-point_src)) comm_val[i]=len_src-point_src;
     point_src+=comm_val[i];
     break;
    case 2: /* Запись частей заданной длины, пока не кончится файл или общее
               количество записанных частей не достигнет 999. */
     do
     {
      if (comm_val[i]>(len_src-point_src)) comm_val[i]=len_src-point_src;
      save_part (argv[2], point_src, comm_val[i]);
      point_src+=comm_val[i]; n_part++;
     } while ((point_src<len_src)&&(n_part<=999));
     break;
    case 3: /* Запись остатка файла. */
     save_part (argv[2], point_src, len_src-point_src);
     point_src=len_src; n_part++;
   }
   /* Если файл уже обработан или номер следующей записываемой части больше
      максимального (999) - заканчиваем выполнение команд. */
   if ((point_src==len_src)||(n_part>999)) break;
  }
 }
 if (fclose(f_src)!=0) printf ("Can't close source file!");
 printf("\n");
}

/* ==========================================================================
   Функция save_part - запись файла-части.
   Вход: nameonly - имя записываемого файла (без расширения),
         n_part - номер записываемой части (по нему формируется расширение),
         begin_part - смещение части от начала исходного файла,
         len_part - длина части.
 ========================================================================= */

void save_part (char *nameonly, long begin_part, long len_part)
{
 long i;
 char dst_ext[4];
 char dst_name[12];

 /* Формируем имя записываемого файла с расширением. */
 itoa (n_part,dst_ext,10);
 strcpy (dst_name, nameonly);
 strcat (dst_name, ".");
 for (i=strlen(dst_ext);i<3;i++) strcat (dst_name,"0");
 strcat (dst_name, dst_ext);

 /* Записываем. */
 printf ("Creating file: %s   ",dst_name);
 f_dst=fopen(dst_name,"wb");
 if (f_dst==NULL) {printf ("Can't open destiny file!\n"); exit(0);}
 fseek (f_src, begin_part, SEEK_SET);
 for (i=0;i<len_part;i++)
 {
  int c;
  c=fgetc(f_src); if (c==EOF) {printf ("Can't read from source file!\n"); exit(0);}
  if (fputc(c,f_dst)==EOF) {printf ("Can't write to destiny file!\n"); exit(0);}
 }
 if (fclose(f_dst)!=0) {printf ("Can't close destiny file!\n"); exit(0);}
 printf ("OK\n");
}

/* ==========================================================================
   Функция work_com - разбор заданных команд.
   Вход: s - строка с командами.
   Выход: 0 - OK, 1 - ошибка в строке с командами.
 ========================================================================= */

int work_com (char *s)
{
m1: /* Главный цикл. */
 /* Строка с командами закончилась? */
 if (*s==0) {if (n_comm==0) return (1); else return (0);}

 /* В массиве ещё есть место для команд? */
 if (n_comm==MAX_COMM) return (1);

 /* Команда началась с числа? */
 if ((*s>='0')&&(*s<='9'))
 {
  if (get_val(&s,&comm_val[n_comm])==1) return (1); /* Ошибка! */
  /* Проверяем, имеется ли за числом символ "-" или "*", и в зависимости
     от этого определяем тип команды. */
  if (*s=='-') {comm_type[n_comm]=1; s++; n_comm++; goto end_comm;}
  if (*s=='*') {comm_type[n_comm]=2; s++; n_comm++; goto end_comm;}
  comm_type[n_comm]=0; n_comm++; goto end_comm;
 }

 /* Команда "*"? */
 if (*s=='*') {comm_type[n_comm]=3; s++; n_comm++; goto end_comm;}

 /* Команда не распознана. */
 return (1); /* Ошибка! */

 /* Текущая команда закончилась. Проверяем: теперь должен быть либо конец
    строки (код 0), либо запятая и далее ненулевой символ (начало следующей
    команды). */
end_comm:
 if (*s==0) goto m1;
 if ((*s==',')&&(*(s+1)!=0)) {s++; goto m1;}
 return (1); /* Ошибка! */
}

/* ==========================================================================
   Функция get_val - преобразование строки с записью числа в это число.
   Число может быть представлено в десятичном или шестнадцатеричном виде
   и должно находиться в диапазоне 0<x<=LONG_MAX.
   Вход: s - адрес указателя на строку, v - адрес, куда поместить число.
   Выход: если число задано корректно, функция возвращает 0, число помещено
   по адресу v, а указатель на строку указывает на первый символ после числа.
   Если число задано некорректно, функция возвращает 1.
 ========================================================================= */

int get_val (char **s, long *v)
{
 int d;   /* Очередная цифра числа. */

 /* Если первый символ строки - '1'-'9', то началось десятичное число. */

 if ((**s>='1')&&(**s<='9'))
 {
  /* Началось десятичное число, обрабатываем. */
  *v=**s-'0';
loop_dec:
  (*s)++;
  if ((**s>='0')&&(**s<='9')) /* Продолжение числа (цифра 0-9)? */
  {
   d=**s-'0';
   /* Добавляем цифру к числу (и следим за переполнением). */
   if (*v>(LONG_MAX/10)) return (1); /* Ошибка! */ else *v*=10;
   if ((LONG_MAX-*v)<d) return (1);  /* Ошибка! */ else *v+=d;
   goto loop_dec;
  }
  return(0); /* OK! */
 }

 /* Если первый символ строки - '0', то началось шестнадцатеричное число
    (десятичное число не может начинаться с нуля). */

 if (**s=='0')
 {
  (*s)++; if ((**s!='x')&&(**s!='X')) return (1); /* Ошибка! */
  *v=0;
loop_hex:
  (*s)++;
  if (((**s>='0')&&(**s<='9'))||((**s>='a')&&(**s<='f'))||((**s>='A')&&(**s<='F')))
  {
   /* Получаем значение очередной цифры числа. */
   if ((**s>='0')&&(**s<='9')) d=**s-'0';
   if ((**s>='a')&&(**s<='f')) d=**s-'a'+10;
   if ((**s>='A')&&(**s<='F')) d=**s-'A'+10;
   /* Добавляем цифру к числу (и следим за переполнением). */
   if (*v>(LONG_MAX/16)) return (1); /* Ошибка! */ else *v*=16;
   if ((LONG_MAX-*v)<d) return (1);  /* Ошибка! */ else *v+=d;
   goto loop_hex;
  }
  /* Число кончилось. Теперь, если *v=0, то либо число равно 0, либо в числе
     не было ни одной цифры. И то и другое недопустимо. */
  if (*v==0) return (1); /* Ошибка! */ else return (0); /* OK! */
 }

 /* Число в строке не обнаружено. */
 return (1); /* Ошибка! */
}
Скачать исполняемый файл программы (для DOS) (7 КБ ZIP)
Скачать листинг программы в текстовом виде (3 КБ ZIP)

Дополнительная информация

Если кто не знает, как выполнять обратную задачу — склеивать файлы из частей, — напоминаю, что это можно сделать с помощью досовской команды «copy». В командной строке надо указать параметр «/b» (двоичное склеивание), имена склеиваемых файлов в нужном порядке, соединённые знаком «+», и имя файла-результата.

Пример:

copy /b 1.txt+2.txt+3.txt all.txt

Обращаю внимание, что получаемый файл не должен совпадать с каким-либо из исходных файлов, иначе содержимое этого исходного файла может быть потеряно, и выполнение команды завершится с ошибкой!

Предупреждение

Описанная в этой статье программа не может обрабатывать файлы, длина которых больше или равна 2 ГБ (2147483648 байтов). Дело в том, что используемая в программе библиотечная функция для определения длины файла (filelength) возвращает результат типа long, а максимальное положительное значение этого типа в Turbo C 2.0 равно 0x7FFFFFFF (2147483647), т.е. 2 ГБ – 1. Также значение типа long требует в качестве входного параметра функция fseek, используемая в программе для перехода к нужной позиции в обрабатываемом файле.

Страница Ивана Рощина > Статьи >