Как работать с файлами больше 2 ГБ - C (СИ)
Формулировка задачи:
Всем доброго времени суток!
Изучал работу с getopt_long и сделал программку для копирования файлов. Ну и дальше ради спортивного интереса начал допиливать её - сделал полоску вывода, оформил вывод размера в метрах/гигах, потом вынес вывод в отдельный поток, чтобы не тормозить копирование выводом... Но не в этом суть.
Чтобы проверить вывод в гигах, надо было скопировать большой файл. Не нашёл ничего лучше образа IL-2 1946. А он, зараза, весит 3 гига с копейками. Запустил - а она мне выводит, мол, весит -1 Б и отстреливается. Не долго думая (знаю, что дурацкая идея) long, в котором я держал размер файла, переделал в unsigned long. Она мне сказала, что файл весит 4 гига ровно, затем скопировала примерно 2 гига и отстрелилась. Подключил мозг и выяснил, что ftell возвращает значение типа long, а в него помещается максимум 2147483647 (если считать это байтами, то в гигабайтах это будет 1,99999...).
Отсюда
Запускал под XP SP2.
вопрос
: как в С полноценно работать с файлами больше 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;
- }
Решение задачи: «Как работать с файлами больше 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;
- }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д