Как работать с файлами больше 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;
}