Как работать с файлами больше 2 ГБ - C (СИ)

Узнай цену своей работы

Формулировка задачи:

Всем доброго времени суток! Изучал работу с getopt_long и сделал программку для копирования файлов. Ну и дальше ради спортивного интереса начал допиливать её - сделал полоску вывода, оформил вывод размера в метрах/гигах, потом вынес вывод в отдельный поток, чтобы не тормозить копирование выводом... Но не в этом суть. Чтобы проверить вывод в гигах, надо было скопировать большой файл. Не нашёл ничего лучше образа IL-2 1946. А он, зараза, весит 3 гига с копейками. Запустил - а она мне выводит, мол, весит -1 Б и отстреливается. Не долго думая (знаю, что дурацкая идея) long, в котором я держал размер файла, переделал в unsigned long. Она мне сказала, что файл весит 4 гига ровно, затем скопировала примерно 2 гига и отстрелилась. Подключил мозг и выяснил, что ftell возвращает значение типа long, а в него помещается максимум 2147483647 (если считать это байтами, то в гигабайтах это будет 1,99999...). Отсюда

вопрос

: как в С полноценно работать с файлами больше 2 гигов? Полноценно - это в смысле не только читать-писать, но и корректно выполнять перемещение по файлу (ftell / fseek). Вот исходник в качестве иллюстрации проблемы:
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <pthread.h>
 
pthread_mutex_t in_mutex = PTHREAD_MUTEX_INITIALIZER;
long in_progress = 0;
long in_size = 0;
 
pthread_mutex_t done_mutex = PTHREAD_MUTEX_INITIALIZER;
char done = 0;
 
void out_size( float size )
{
    if( size < 1024.0f )
       printf( " %.0f B", size );
    else if( size < 1048576.0f )
       printf( " %.2f KB ", size / 1024.0f );
    else if( size < 1073741824.0f )
       printf( " %.2f MB ", size / 1048576.0f );
    else
       printf( " %.2f GB ", size / 1073741824.0f );
}
 
void progress_thread( void * p )
{
   const unsigned short bar_resolution = 40;
 
   char buffer[ bar_resolution ];
   const unsigned delay = 100000;
 
   char l_done = 0;
   char spinner = '\\';
 
   do
   {
       /*  ------- скопировать глобальные значения прогресса и размера файла в локальные переменные -------  */
 
       if( 0 != pthread_mutex_trylock( &in_mutex ) )
       {
           usleep( delay );
           continue;
       }
 
       long l_in_progress = in_progress;
       long l_in_size = in_size;
 
       pthread_mutex_unlock( &in_mutex );
 
       /*  ------- проверить необходимость прерывания вывода -------  */
 
       pthread_mutex_lock( &done_mutex );
 
       if( done == 1 ) /* копирование завершено */
       {
           l_in_progress = l_in_size;
           l_done = 1;
       }
       else if ( done > 1 ) /* копирование прервано ошибкой */
       {
           pthread_mutex_unlock( &done_mutex );
           break;
       }
 
       pthread_mutex_unlock( &done_mutex );
 
       /*  ------- вывести -------  */
 
       unsigned short progress = (float)l_in_progress / (float)l_in_size * 100.0f;
       unsigned short chars = (float)progress / 100.0f * (float)bar_resolution;
 
       printf( "\r%i%% [", progress );
 
       unsigned short i = 0;
       char *ptr = buffer;
 
       for( ; i < bar_resolution; i++, ptr++ )
       {
           if( i < chars )
             *ptr = '-';
           else if( i == chars )
             *ptr = spinner;
           else
             *ptr = ' ';
       }
 
       *ptr = 0;
 
       printf( "%s]", buffer );
 
       out_size( l_in_progress );
       putchar( '/' );
       out_size( l_in_size );
       printf( "        " );
 
       /*  ------- если необходимо прервать вывод, вывалиться из цикла -------  */
 
       if( l_done > 0 )
           break;
 
       if( spinner == '\\' ) spinner = '|';
       else if( spinner == '|' ) spinner = '/';
       else if( spinner == '/' ) spinner = '-';
       else spinner = '\\';
 
       usleep( delay );
   }
   while( 1 );
 
   pthread_exit( 0 );
}
 
inline void helpOut()
{
    puts( "This program copies contents of one file into another\n"
         "\t-i, --input-file - specify INPUT file after this\n"
         "\t-o, --output-file - specify OUTPUT file after this\n"
         "\t-n, --no-output - do not produce output (except error messages)\n"
         "\t-h, --help - display this help message" );
}
 
const char * opt_str = "i:o:hn";
const struct option opts[] =
{
    { "input-file", required_argument, 0, 'i' },
    { "output-file", required_argument, 0, 'o' },
    { "help", no_argument, 0, 'h' },
    { "no-output", no_argument, 0, 'n' },
    { 0, no_argument, 0, 0 }
};
 
int main( int argc, char ** argv )
{
    char * inFile = 0,
         * outFile = 0;
 
    char output = 1;
 
    int opt = getopt_long( argc, argv, opt_str, opts, 0 );
 
    while( opt != -1 )
    {
        switch( opt )
        {
            case 'i':
               inFile = optarg;
               break;
 
            case 'o':
               outFile = optarg;
               break;
 
            case 'h':
               helpOut();
               break;
 
            case 'n':
               output = 0;
               break;
        }
 
        opt = getopt_long( argc, argv, opt_str, opts, 0 );
    }
 
    FILE * f_in = fopen( inFile, "rb" );
 
    if( !f_in )
    {
       fprintf( stderr, "Error opening file \"%s\" for input: ", inFile );
       perror( 0 );
       return -1;
    }
 
    FILE * f_out = fopen( outFile, "wb" );
 
    if( !f_out )
    {
       fprintf( stderr, "Error opening file \"%s\" for output: ", outFile );
       perror( 0 );
       fclose( f_in );
       return -1;
    }
 
    /* узнать размер файла */
    fseek( f_in, 0, SEEK_END );
    in_size = ftell( f_in );
    fseek( f_in, 0, SEEK_SET );
 
    pthread_t id;
 
    if( output )
    {
       puts( "Copying:" );
       pthread_create( &id, 0, progress_thread, 0 );
    }
 
    const unsigned block_size = 4096;
 
    char buffer[ block_size ];
 
    while( in_progress < in_size )
    {
       long int size = in_size - in_progress > block_size ? block_size : in_size - in_progress;
 
       if( size != fread( buffer, 1, size, f_in ))
       {
           perror( "\nCould not read input file" );
 
           if( output ) pthread_mutex_lock( &done_mutex );
           done = 2; /* прервать поток вывода с ошибкой */
           if( output ) pthread_mutex_unlock( &done_mutex );
 
           break;
       }
 
       if( size != fwrite( buffer, 1, size, f_out ) )
       {
          perror( "\nCould not write to output file" );
 
          if( output ) pthread_mutex_lock( &done_mutex );
          done = 2; /* прервать поток вывода с ошибкой */
          if( output ) pthread_mutex_unlock( &done_mutex );
 
          break;
       }
 
       if( output ) pthread_mutex_lock( &in_mutex );
       in_progress = ftell( f_in );
       if( output ) pthread_mutex_unlock( &in_mutex );
    }
 
    if( output && done == 0 )
    {
       pthread_mutex_lock( &done_mutex );
       done = 1; /* прервать поток вывода */
       pthread_mutex_unlock( &done_mutex );
    }
 
    fclose( f_out );
    fclose( f_in );
 
    if( output )
    {
       pthread_join( id, 0 );
       puts( "\nDone." );
    }
 
    if( done == 2 ) /* в случае ошибки во время копирования */
       return 1;
 
    return 0;
}
Запускал под XP SP2.

Решение задачи: «Как работать с файлами больше 2 ГБ»

textual
Листинг программы
#include <iostream>
#include <wx/wx.h>
#include <wx/file.h>
#include <wx/ffile.h>
#include <wx/filename.h>
 
int main(int argc, char* argv[])
{
    setlocale(LC_ALL, "");
    wxInitialize(argc, argv);
#ifdef wxHAS_LARGE_FFILES
    std::cout << "wxHAS_LARGE_FFILES" << std::endl;
#else // !wxHAS_LARGE_FFILES
    std::cout << "No Large files support with wxFFile" << std::endl;
#endif // wxHAS_LARGE_FFILES
#ifdef wxHAS_LARGE_FILES
    std::cout << "wxHAS_LARGE_FILES" << std::endl;
#else // !wxHAS_LARGE_FILES
    std::cout << "No Large files support with wxFile" << std::endl;
#endif // wxHAS_LARGE_FILES
 
    wxFileName filename(wxT("test.dat"));
 
    wxString str;
    wxFileOffset pos;
    if (filename.FileExists())
    {
        std::cout << "File size: " 
            << filename.GetSize().ToString().mb_str()
            << std::endl;
#ifdef wxHAS_LARGE_FFILES
        // Файлы с буферизацией (обёртка над FILE)
        wxFFile ffile(filename.GetFullPath());
        ffile.SeekEnd();    
        pos = ffile.Tell();
        str.Printf(wxT("wxFFile end pos: %") wxFileOffsetFmtSpec wxT("d"), pos);
        std::cout << str.mb_str() << std::endl;
        ffile.Close();
#endif // wxHAS_LARGE_FFILES
    
#ifdef wxHAS_LARGE_FILES
        // "Сырые" файлы
        wxFile file(filename.GetFullPath());
        file.SeekEnd(); 
        pos = file.Tell();
        str.Printf(wxT("wxFile end pos: %") wxFileOffsetFmtSpec wxT("d"), pos);
        std::cout << str.mb_str() << std::endl;
        file.Close();
#endif // wxHAS_LARGE_FILES
    }
    else
    {
        std::cout << "The file does not exist" <<std::endl; 
    }   
 
    wxUninitialize();
    return 0;
}

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

11   голосов , оценка 4.091 из 5