Консультация № 179077
13.06.2010, 15:46
0.00 руб.
0 5 2
Здравствуй те Эксперты.
у меня возникла проблема при написанние программы на С++ Билдере 6.
сама задача такая "В существующем на диске файле поменять местами первый из наиболее часто встречающихся символов с первым из наиболее редко встречающихся символов. (Файл создать с помощью текстового редактора)
Вспомогательный файл не использовать."
Как я поняла, необходимо чтобы он проверил файл на все 26 символов, и нашел самый большой.
Для того чтобы как это проверть нужно их занести в переменную какую нибудь и сравнить и + доп.открыть счетчик проверки этих символов.
и в конце уже менять местами,самый большой с самым маленьким.
Если я не ошибаюсь всего 26 символов, плюс еще придется потратить 52 буквы(переменных) чтобы все это произвести.
возможно ли это как нибудь сократить?
Если да, то как образом.?

Обсуждение

Неизвестный
13.06.2010, 21:21
общий
это ответ
Здравствуйте, Илона Меньшикова.
Цитата: Илона Меньшикова
Если я не ошибаюсь всего 26 символов, плюс еще придется потратить 52 буквы(переменных) чтобы все это произвести.

Нет, можно проще. Узнайте размер файла, выделите массив нужного размера.
Далее в примере показано, как найти в массиве первый наиболее часто и наименее часто повторяющиеся элементы.
Код:
#include <stdio.h>
#include <conio.h>
#include <string.h>

int main()
{
char massiv[15]="hheeeerrrrrddq";
int size_of_massiv=strlen(massiv);
int current_max,i,j,max,min,current_min;
int symbol_position,symbol_position2;
max=0; min=size_of_massiv+1;
current_min=0;
current_max=0;
//поиск первого наиболее часто встречающегося элемента
for (i=0; i<size_of_massiv; i++)
{
for (j=0; j<size_of_massiv;j++)
if (massiv[i]==massiv[j]) current_max++;
if (current_max>max) {
max=current_max;
symbol_position=i;
}
current_max=0;
}
//поиск первого наименее часто встречающегося элемента
for (i=0; i<size_of_massiv; i++)
{
for (j=0; j<size_of_massiv;j++)
if (massiv[i]==massiv[j]) current_min++;
if (current_min<min) {
min=current_min;
symbol_position2=i;
}
current_min=0;
}
printf("%c = %d times \n",massiv[symbol_position],max);
printf("%c = %d times \n",massiv[symbol_position2],min);
getch();
}
5
без коментарев
Неизвестный
13.06.2010, 21:27
общий
мм а если это написанно в файле и мы должны это считать с него.
и потом заменить и потом это вывести на экран.?
тоесть дана строчка из чего либа, и в ней поменять местами часто встречающейся буквы.
Неизвестный
14.06.2010, 00:48
общий
Илона Меньшикова:
Считать данные файла посимвольно в массив. Потом просто поменять местами элементы massiv[symbol_position] и massiv[symbol_position2]. А затем получившийся массив записать в исходный файл. А на экран можно вывести позицию измененного символа в файле.. (symbol_position, symbol_position2). Я бы сделала так.
Неизвестный
14.06.2010, 14:54
общий
это ответ
Здравствуйте, Илона Меньшикова.

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

Код:
/*
В существующем на диске файле поменять местами первый из наиболее
часто встречающихся символов с первым из наиболее редко встречающихся
символов. (Файл создать с помощью текстового редактора)
Вспомогательный файл не использовать.
*/

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#include <string.h>

// всю обработку файла выполняем в этой функции
int work( const char* pszFileName )
{
// открываем файл для чтения/записи с разделением доступа по чтению
int h = _open( pszFileName, _O_BINARY | _O_RDWR, _S_IREAD );
if( h == -1 ) return -1;

unsigned freq[256]; // кол-во соответствующих символов
unsigned long offs[256]; // где первый раз встретился символ
memset( freq, 0, sizeof freq ); // обнуляем массив частот символов
memset( offs, -1, sizeof offs ); // заполняем offs значением (-1)

unsigned char buf[4096]; // буфер для чтения файла
unsigned long offs_buf = 0; // файловое смещение начала буфера
int iRetValue = 0; // возвращаемое значение

while(1) {
int n = _read( h, buf, sizeof buf );
if( n < 0 ) { iRetValue = -2; break; } // ошибка чтения
if( n == 0 ) break; // конец файла

for( int i = 0; i < n; ++i ) { // сканируем буфер
int ch = buf[i]; // символ
++freq[ch]; // счетчик вхождений этого символа
if( offs[ch] == -1 ) // если символ встретился первый раз,
offs[ch] = offs_buf + i; // то запоминаем его смещение в файле

}
offs_buf += n; // файловое смещение следующего блока; в конце - размер файла
}

if( iRetValue == 0 ) {
// определяем самый частый и самый редкий символ среди имеющихся в файле
int chMin, chMax;
unsigned long nMin = ULONG_MAX, nMax = 0;
for( int i = 0; i < 256; ++i ) {
int f = freq[i];
if( f ) {
if( f > nMax ) {
nMax = f; // максимальная частота
chMax = i; // соответствующий символ
}
else if( f < nMin ) {
nMin = f; // минимальная частота
chMin = i; // соответствующий символ
}
}
}

if( nMin == ULONG_MAX )
iRetValue = -4; // весь файл заполнен одним символом (chMax)
else if( offs_buf >= 3 ) {
printf(
"Наиболее часто встречается символ '%c' (\\x%02x), первое вхождение в позиции %lu\n"
"Наиболее редко встречается символ '%c' (\\x%02x), первое вхождение в позиции %lu\n",
chMax, chMax, offs[chMax],
chMin, chMin, offs[chMin] );

_lseek( h, offs[chMin], 0 ); // позиция самого редкого символа
_write( h, &chMax, 1 ); // записываем самый частый символ

_lseek( h, offs[chMax], 0 ); // позиция самого частого символа
_write( h, &chMin, 1 ); // записываем самый редкий символ
}
else
iRetValue = -3;
}
_close(h);
return iRetValue;
}

int main( int argc, char* argv[] )
{
char szInput[_MAX_PATH];
char *pInName;
if( argc >= 2 ) // если в командной строке переданы параметры,
pInName = argv[1]; // то первый из них - имя обрабатываемого файла
else { // иначе - запрашиваем имя файла у пользователя
printf( "Введите имя исходного файла: " );
pInName = gets( szInput );
}

int iResult = work( pInName );
switch( iResult ) {
case -1:
printf( "Ошибка при открытии файла "%s"\n", pInName );
break;
case -2:
printf( "Ошибка при чтении файла "%s"\n", pInName );
break;
case -3:
printf( "Слишком маленький файл "%s"\n", pInName );
break;
case -4:
printf( "Весь файл заполнен одним символом.\n" );
break;
}
return iResult;
}


За неимением C++ Builder программа протестирована в MSVC++ 6.0 и Code::Blocks 10.05.

Успехов!
5
Неизвестный
14.06.2010, 22:09
общий
Илона Меньшикова:
Хочу сделать небольшое замечание к своей программе.

массив смещений у меня объявлен как
Код:
unsigned long offs[256];	// где первый раз встретился символ

далее в цикле есть сравнение:
Код:
if( offs[ch] == -1 )			// если символ встретился первый раз,

Вообще-то, такое сравнение работает нормально, но выглядит не слишком хорошо.
можно изменить так:
Код:
if( offs[ch] == (unsigned long)(-1) )			// если символ встретился первый раз,

или так:
Код:
if( offs[ch] == 0xFFFFFFFF )			// если символ встретился первый раз,

А можно и вообще обойтись без предварительного заполнения массива offs значениями -1:
Код:

for( int i = 0; i < n; ++i ) { // сканируем буфер
int ch = buf[i]; // символ
if( freq[ch] == 0 ) // если символ встретился первый раз,
offs[ch] = offs_buf + i; // то запоминаем его смещение в файле
++freq[ch]; // счетчик вхождений этого символа
}

И это, наверное, будет наилучшим вариантом. Что-то я лопухнулся малость
Форма ответа