Как работать с файлами больше 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; }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д