Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > IT форум > Помощь студентам
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 27.05.2012, 17:48   #1
MooNDeaR
В стагнации
Участник клуба
 
Аватар для MooNDeaR
 
Регистрация: 29.07.2011
Сообщений: 1,303
По умолчанию Чтение BMP-файла "руками" [Cи]

Есть задача с помощью средств самого Си считать монохромный BMP-файл размером 320x240 (9600 байт). Естественно я сразу полез на Википедию, дабы узнать как устроен формат.
Итак, собственно вопрос: при попытке считать первую же структуру BITMAPFILEHEADER из файла получается фэйл. Вот текст самой программы.

Код:
#include <stdio.h>

struct bmpheader
{
	unsigned char	b1,b2;			//Символы BM (смение 0, длина 2)
	unsigned long 	bfSize;			//Размер файла в байтах	(Смещение 2, длина 4)
	unsigned short	bfReserved1;	        //Бесполезно (Смещение 6, длина 2)
	unsigned short	bfReserved2;	        //Бесполезно (Смещение 8, длина 2)
	unsigned long	bfOffBits;		//Смещение до самого изображения (Смещение 10, длина 4)
};

int main()
{
	FILE* f;

	bmpheader hdBMP;

	if((f = fopen("Mono.bmp","rb")) == NULL)
		return -1;
	
	fread(&hdBMP,sizeof(hdBMP),1,f);
	
	printf("%c%c\n",hdBMP.b1,hdBMP.b2);
	printf("%d\n",hdBMP.bfSize);
	printf("%d\n",hdBMP.bfReserved1);
	printf("%d\n",hdBMP.bfReserved2);
	printf("%d\n",hdBMP.bfOffBits);

	return 0;
}
При этом результат выполнения будет такой:
Цитата:
BM
0
0
62
2621440
И это очевидно неправильно. Во-первых размер файла 0. Я решил тип поля bfSize заменить на unsigned short. Результат был таким
Цитата:
BM
9662
0
0
4063232
Почему получается все поля принимают правильные значение, а поле bfOffBits такое кривое? Не могу понять
E-mail: pashaworking@gmail.com | ICQ: 479914426 | Skype: moondearr
Понять, чего от тебя требует заказчик – это уже половина всей работы, а иногда и полностью выполненное задание.

Последний раз редактировалось MooNDeaR; 27.05.2012 в 21:55.
MooNDeaR вне форума Ответить с цитированием
Старый 27.05.2012, 19:42   #2
s-andriano
Старожил
 
Аватар для s-andriano
 
Регистрация: 08.04.2012
Сообщений: 3,229
По умолчанию

На С не пишу, так что сразу прошу извинить за, возможно, глупые вопросы.

Взял первый попавшийся BMP-файл и заглянул в него HEX-редактором - все правильно, значение 36h, т.е. 54 - как и положено.

Может, объявленная Вами структура имеет выравнивание на 4 байта?
Прочитайте первые 14 байт побайтно и выведите результат.

PS. Честно говоря, я первые 14 байт при чтении всегда пропускаю - в них все равно нет ничего интересного.

PPS. Да, еще, выведите размер структуры. На Паскале это было бы sizeof(bmpheader), как на С - не знаю, может, так же.

Последний раз редактировалось s-andriano; 27.05.2012 в 19:46.
s-andriano вне форума Ответить с цитированием
Старый 27.05.2012, 20:03   #3
MooNDeaR
В стагнации
Участник клуба
 
Аватар для MooNDeaR
 
Регистрация: 29.07.2011
Сообщений: 1,303
По умолчанию

Не знаю где глюк, наверное в википедии, но после замены структуры на такой вид:
Код:
struct bmpheader
{
	unsigned char	b1,b2;			//Символы BM (смение 0, длина 2)
	unsigned short	bfSize;			//Размер файла в байтах	(Смещение 2, длина 2)
	unsigned short	bfReserved1;	        //Бесполезно (Смещение 6, длина 2)
	unsigned short	bfReserved2;	        //Бесполезно (Смещение 8, длина 2)
	unsigned short  padding;		//Мусор, для выравнивания
	unsigned short	bfOffBits;		//Смещение до самого изображения (Смещение 10, длина 2)
};
всё внезапно заработало правильно.
E-mail: pashaworking@gmail.com | ICQ: 479914426 | Skype: moondearr
Понять, чего от тебя требует заказчик – это уже половина всей работы, а иногда и полностью выполненное задание.
MooNDeaR вне форума Ответить с цитированием
Старый 27.05.2012, 20:57   #4
s-andriano
Старожил
 
Аватар для s-andriano
 
Регистрация: 08.04.2012
Сообщений: 3,229
По умолчанию

Повторю свою рекомендацию выяснить размер структуры. Если она окажется равной 18, а не 14 байтам - дело в выравнивании.
Я рекомендую все-таки разобраться с этим, потому что после заголовка файла будет заголовок изображения, в котором тоже чередуются поля разного размера.
s-andriano вне форума Ответить с цитированием
Старый 27.05.2012, 21:52   #5
MooNDeaR
В стагнации
Участник клуба
 
Аватар для MooNDeaR
 
Регистрация: 29.07.2011
Сообщений: 1,303
По умолчанию

Узнал размер структуры. Она выравнивается до 16 байт. Что всё равно не объясняет кривое считывание первых данных, ведь теоретически выравнивание дописывает байты к концу структуры, что испортило бы мне считывание следующей структуры BITMAPINFOHEADER, но никак не Хедера.

Нашел причину в кривой работе функции fread(). Она ОЧЕНЬ странно как-то считает данные. Не более двух байт за раз. Получаются вот такие перлы:
Есть последовательность байтов: 00000000 00000000 00000000 00101000 (число 40 в формате ulong)

Если записать структуру так:
Код:
struct Test
{
	unsigned short X;
	unsigned short Y;
} T;
Затем считать её фунцией fread() вот таким образом:
Код:
fread(&T,4,1,file);
а затем посмотреть значения X и Y, то получим, как и ожидали значения 0 (первые 2 байта нули) и 40 (вторые два байта).

НО! Вот если эту же структуру записать так:
Код:
struct Test
{
	unsigned long X;
}
и считать те же четыре байта, то должны получить, теоретически, число 40. Или я не прав? Что же мы получаем на самом деле (просмотрел двоичный вид числа):
Цитата:
00000000 00101000 00000000 00000000
Т.е. получается он сначала считал первые 2 байта и записал их в конец, потом еще 2 байта и записал их "сверху тех" и в результате получается число 2621440.

Что за шутки я не знаю, и понятия пока не имею как правильно это обходить

P.S.
Посмотрел на всё это дело из HEX-редактора.
Все числа оказывается записаны с конца, т.е. от младшего байта, к старшему. Т.е. поледовательность:
Код:
00000000 00000000 00000000 00101000
записана как:
Код:
00101000 00000000 00000000 00000000
.

Тобишь, согласно Википедии порядок байтов идет как Little-endian. Тогда мне вообще непонятен принцип работы функции fread()...
E-mail: pashaworking@gmail.com | ICQ: 479914426 | Skype: moondearr
Понять, чего от тебя требует заказчик – это уже половина всей работы, а иногда и полностью выполненное задание.

Последний раз редактировалось MooNDeaR; 27.05.2012 в 22:11.
MooNDeaR вне форума Ответить с цитированием
Старый 27.05.2012, 22:12   #6
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Физически 40 для long на диске записано так: 00101000 00000000 00000000 00000000
при правильном чтении должно получится так: 00000000 00000000 00000000 00101000
т.е. слова меняются местами и внутри слов байты меняются местами. 100% проблемы выравнивания. На Си не пишу поэтому дальше пас. Хотя слышал о директиве препроцессора #pragma. Может в эту сторону копнуть?
Могу еще ссылочку подкинуть http://jenyay.net/Programming/Bmp
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию

Последний раз редактировалось Аватар; 27.05.2012 в 22:20.
Аватар вне форума Ответить с цитированием
Старый 27.05.2012, 22:21   #7
MooNDeaR
В стагнации
Участник клуба
 
Аватар для MooNDeaR
 
Регистрация: 29.07.2011
Сообщений: 1,303
По умолчанию

Цитата:
Физически 40 для long на диске записано так: 00101000 00000000 00000000 00000000
при правильном чтении должно получится так: 00000000 00000000 00000000 00101000
Да, я понимаю. Но в результате считывания ровно 4-й байт, получается вот это:

00000000 00101000 00000000 00000000

Т.е. функция fread() сначала правильно считывает первые 2 байта (переворачивает), затем вторые 2 байта и тоже переворачивает дописывая к концу типа unsigned long совсем не в тему эти нули, хотя надо было бы их дописывать к началу числа.

Цитата:
слышал о директиве препроцессора #pragma
Есть такая, но, насколько помню, в стандарт не входит, а работает только в некоторых IDE (Builder, MS Studio). Видел где-то на англоязычных форумах обсуждалась данная байда, и использовали как раз #pragma, но вот только я, если честно, нефига не понял. И еще мне непонятны пляски с этой fread().
E-mail: pashaworking@gmail.com | ICQ: 479914426 | Skype: moondearr
Понять, чего от тебя требует заказчик – это уже половина всей работы, а иногда и полностью выполненное задание.
MooNDeaR вне форума Ответить с цитированием
Старый 27.05.2012, 22:25   #8
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Цитата:
получается вот это
Скорее всего это из-за сдвига при чтении влево на 2 байта, в которых нули
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию
Аватар вне форума Ответить с цитированием
Старый 27.05.2012, 22:33   #9
MooNDeaR
В стагнации
Участник клуба
 
Аватар для MooNDeaR
 
Регистрация: 29.07.2011
Сообщений: 1,303
По умолчанию

Спасибо за совет про директиву #pragma.
Решил проблему вот таким образом:
Код:
#pragme pack(push)
#pragma pack(1)
struct bmpheader
{
	unsigned char	b1,b2;			//Символы BM (смение 0, длина 2)
	unsigned long	bfSize;			//Размер файла в байтах	(Смещение 2, длина 4)
	unsigned short	bfReserved1;	        //Бесполезно (Смещение 6, длина 2)
	unsigned short	bfReserved2;	        //Бесполезно (Смещение 8, длина 2)
	unsigned long	bfOffBits;		//Смещение до самого изображения (Смещение 10, длина 4)
};
#pragma pack(pop)
Правда, если честно, понятия не имею что я сделал, sizeof(bmpheader) возвращает 14. Просто порылся на англоязычных форумах. Буду читать. Еще раз спасибо.
E-mail: pashaworking@gmail.com | ICQ: 479914426 | Skype: moondearr
Понять, чего от тебя требует заказчик – это уже половина всей работы, а иногда и полностью выполненное задание.

Последний раз редактировалось MooNDeaR; 27.05.2012 в 23:07.
MooNDeaR вне форума Ответить с цитированием
Старый 27.05.2012, 23:37   #10
s-andriano
Старожил
 
Аватар для s-andriano
 
Регистрация: 08.04.2012
Сообщений: 3,229
По умолчанию

Цитата:
Сообщение от Аватар Посмотреть сообщение
Физически 40 для long на диске записано так: 00101000 00000000 00000000 00000000
при правильном чтении должно получится так: 00000000 00000000 00000000 00101000
т.е. слова меняются местами и внутри слов байты меняются местами. 100%
Ничего подобного, что на диске, что в памяти на архитектуре Intel всегда Little-Endian.
Ничто ни с чем не меняется, все как было, так и остается.
s-andriano вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Натолкните на алгоритм или "Ночной город своими руками" Karp_13 Общие вопросы C/C++ 18 12.03.2012 01:05
чтение и отображение из ".txt" файла в dataGridView Leva_89 C# (си шарп) 0 23.03.2011 23:23
Чтение из ZIP-файла "на лету" (и ещё BASS/BASSMOD) Wanderer_r Мультимедиа в Delphi 3 20.03.2010 21:14
Считывание строк из файла. перемещение по строкам "веред"-"назад" Larkin. Помощь студентам 2 15.06.2009 17:58
"Hide Folder" своими руками (Delphi) Air Помощь студентам 3 13.11.2008 03:38