Приветствую. Столкнулась с необходимостью считать бинарный файл побайтово перлом. Файл содержит некоторый кодированный текст в неASCII кодировке. Каждый символ представлен 8 разрядным двоичным числом (т.е. как я поняла как раз и должен быть байтом). По условию задачи символы дублируются для надежности передачи, но это не суть важно.Пытаюсь считать так:
#! /bin/usr/perl
open(file1, "text.txt");
binmode(file1);
$i=1;
while (($n = read file1, @dimens1[$i], 1) !=0) {
@dimens2[$i] = bin2dec (@dimens1[$i]);
print "Bytes readed $n Number of read $i deccode @dimens2[$i] bincode " . dec2bin(@dimens2[$i])."\n";
$i++;
};
die();
sub bin2dec {
return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
};sub dec2bin {
my $str = unpack("B32", pack("N", shift));
$str =~ s/^0+(?=d)//; # otherwise you'll get leading zeros
return $str;
};Получаю примерно следующее:
Bytes readed 1 Number of read 7893 deccode 0 bincode 00000000000000000000000000000000
Bytes readed 1 Number of read 7894 deccode 1 bincode 00000000000000000000000000000001
Bytes readed 1 Number of read 7895 deccode 1 bincode 00000000000000000000000000000001Т.е. считывается почему-то не байт, а только 1 бит из байта. Если считывать по 8 байт, то получим следующий вывод:
Bytes readed 8 Number of read 46395 deccode 155 bincode 00000000000000000000000010011011
Bytes readed 8 Number of read 46396 deccode 173 bincode 00000000000000000000000010101101
Bytes readed 8 Number of read 46397 deccode 189 bincode 00000000000000000000000010111101
Bytes readed 8 Number of read 46398 deccode 109 bincode 00000000000000000000000001101101
что тоже не верно, т.к. по сути теряется 7/8 информации. В итоге из файла размером 371 184 байт получается 46398 8ми разрядных двоичных значений, т.е. явно не то что нужно.32 разрядное двоичное число это издержки производства функции, которая преобразует это в читабельный вид. Это не влияет на результат.
Ковыряла кучу доков, манов и примеров, ничего не получается. Пробовала и с unpack - результат ровно тот же. Что я делаю не так?
> Приветствую. Столкнулась с необходимостью считать бинарный файл побайтово перлом. Файлбольшой вопрос - чтоже тут подразумевается под "бинарностью"
> содержит некоторый кодированный текст в неASCII кодировке. Каждый символ представлен 8
> разрядным двоичным числом (т.е. как я поняла как раз и должен
> быть байтом).может уже заглянуть в сам файл? )) и посмотреть что там и как представлено
>По условию задачи символы дублируются для надежности передачи, но
> это не суть важно.
> большой вопрос - чтоже тут подразумевается под "бинарностью"обычный бинарный файл. :)
>> содержит некоторый кодированный текст в неASCII кодировке. Каждый символ представлен 8
>> разрядным двоичным числом (т.е. как я поняла как раз и должен
>> быть байтом).
> может уже заглянуть в сам файл? )) и посмотреть что там и
> как представленоМожно. В архиве файл http://ifolder.ru/23353276.
> Приветствую. Столкнулась с необходимостью считать бинарный файл побайтово перлом. Файл
> содержит некоторый кодированный текст в неASCII кодировке. Каждый символ представлен 8
> разрядным двоичным числом (т.е. как я поняла как раз и должен
> быть байтом).
> Ковыряла кучу доков, манов и примеров, ничего не получается. Пробовала и с
> unpack - результат ровно тот же. Что я делаю не так?Возможно я не понял задачу, но вы делаете в своем скрипте что-то странное...и нехорошее...
Вот таким образом можно считать файл от начала - до конца побайтно, с преобразованием каждого считанного байта в двоичную форму.
Вне зависимости от кодировки символов, в файле- данные хранятся в байтах. Вы не можете считать меньше одного байта за раз. Больше - можете...Меньше- нет.
Потом, после считывания байта- вы можете делать с ним что угодно...например учесть только один бит из него, пронумеровать отдельные биты и так далее..Использование функции ord обусловлено тем, что когда перл считал байт из файла, он представляет его себе как char ( то есть как символ), а dec2bin-работает с численным представлением. Автоматического преобразования типа при этом не происходит, поэтому таковое преобразование указывается непосредственно - через ord.
Функция dec2bin - делает следующую вещь - она преобразует некое число, в его двоичное представление при этом нолики и единички этого представления будут отдельными БАЙТАМИ.
Например число 5 (один байт, десятичный 5 шестнадцатиричный \х5) после преобразованаия этой функцией превратится в 101 (три БАЙТА), символ же 5 (один байт. десятичный 53 шестнадцатиричный \х35) после преобразованаия этой функцией превратится в 110101 (шесть байт). Число и символ 5 взяты для примера, причем если преобразование числа 5 не зависит от кодировки, то символ 5 - кодировкозависим, то что символ 5 имеет в числовом представлении десятичное 53- характерно только для ASCII-производных кодировок (например cp1251,koi-8 и так далее), но вы конечно можете создать свою кодировку, в которой это число может быть иным.
#! /bin/usr/perl
open(file1, "text.txt");
binmode(file1);
$i=1;while (not eof file1) {
$a=getc file1; #Считываем ОДИН байт из файла. Абсолютно безразлично в какой он кодировке.
print "Number of read $i deccode $a bincode " . dec2bin(ord($a))."\n";
$i++; }sub dec2bin {
my $str = unpack("B32", pack("N", shift));
#$str =~ s/^0+(?=\d)//; # раскоментировать чтоб убрать начальные нули
return $str;
}PS. вы случайно не на информационной защите учитесь? Писец выпуск будет...
>Возможно я не понял задачу, но вы делаете в своем скрипте что-то странное...и >нехорошее...Сейчас попробую объяснить что я вообще делаю.
>Вот таким образом можно считать файл от начала - до конца побайтно, с преобразованием >каждого считанного байта в двоичную форму.
>Вне зависимости от кодировки символов, в файле- данные хранятся в байтах. Вы не можете >считать меньше одного байта за раз. Больше - можете...Меньше- нет.Это я знаю.
>Потом, после считывания байта- вы можете делать с ним что угодно...например учесть >только один бит из него, пронумеровать отдельные биты и так далее..
Ну это не нужно. Просто считать байт и перевести его в двоичную форму.
>Использование функции ord обусловлено тем, что когда перл считал байт из файла, он >представляет его себе как char ( то есть как символ), а dec2bin-работает с численным >представлением. Автоматического преобразования типа при этом не происходит, поэтому >таковое преобразование указывается непосредственно - через ord.
Странно, доки говорят, что при использовании read мы получаем скаляр, и с ним можно работать без преобразования типа.
>Функция dec2bin - делает следующую вещь - она преобразует некое число, в его двоичное >представление п
Эта функция вообще ненужна. Добавила только что бы вывод был хотя бы более - менее читабельным. Ибо мордочки обычного представления переменной содержащей бинарное значение малоинфоррмативны. :) У меня задача на данный момент просто считать файл побайтово и преобразовать каждый байт в десятичную систему. Сколько переменные будут занимать байт после преобразования меня уже мало волнует. Ещё раз повторюсь, что массив используется для дальнейшего решения.
>$a=getc file1; #Считываем ОДИН байт из файла. Абсолютно безразлично в какой он кодировке.
>print "Number of read $i deccode $a bincode " . dec2bin(ord($a))."\n";
>$i++; }
>
>sub dec2bin {
> my $str = unpack("B32", pack("N", shift));
> #$str =~ s/^0+(?=\d)//; # раскоментировать чтоб убрать начальные нули
> return $str;
>}
>К сожалению, Ваша программа выдает ровно тот же результат что и моя. bin2dec я использовала для преобразования в десятичную систему - дальше нужна для решения. Просто скрипт пока недописан из-за загвоздки со считыванием.
Вывод который я получаю с Вашим кодом:
Number of read 371181 deccode bincode 1
Number of read 371182 deccode bincode 1
Number of read 371183 deccode bincode 0
Number of read 371184 deccode bincode 1Это явно не 1 байт.
>PS. вы случайно не на информационной защите учитесь? Писец выпуск будет...
Случайно нет. Я не занималась программированием уже лет 7, на перле писала один раз только парсер конфигов, но там регекспы.
По поводу Вашего кода.>#! /bin/usr/perl
>open(file1, "text.txt");
>binmode(file1);
>$i=1;
>
>while (not eof file1) {
>$a=getc file1; #Считываем ОДИН байт из файла. Абсолютно безразлично в какой он кодировке.Тут Вы считываете 1 байт, это понятно.
>print "Number of read $i deccode $a bincode " . dec2bin(ord($a))."\n";
Эта строчка мне совсем непонятна если честно - Там где bincode Вы вызываете функцию преобразования в 2ичную систему из десятичной если верить cookbook которую я и использовала, но я не вижу преобразования в в 10тичную систему, т.е. функция тут мне кажется странной.
>$i++; }
>
>sub dec2bin {
> my $str = unpack("B32", pack("N", shift));
> #$str =~ s/^0+(?=\d)//; # раскоментировать чтоб убрать начальные нули
> return $str;
>}В общем, код выдает те же самые результаты что и мой если если считывать через read file1, $dimens1[$i], 1 .
>[оверквотинг удален]
>>$i=1;
>>
>>while (not eof file1) {
>>$a=getc file1; #Считываем ОДИН байт из файла. Абсолютно безразлично в какой он кодировке.
> Тут Вы считываете 1 байт, это понятно.
>>print "Number of read $i deccode $a bincode " . dec2bin(ord($a))."\n";
> Эта строчка мне совсем непонятна если честно - Там где bincode Вы
> вызываете функцию преобразования в 2ичную систему из десятичной если верить cookbook
> которую я и использовала, но я не вижу преобразования в в
> 10тичную систему, т.е. функция тут мне кажется странной.Это оно? :
#! /bin/usr/perl
open(file1, "text.txt");
binmode(file1);
$i=1;while (not eof file1) {
$a1=ord getc file1;
$a2=ord getc file1;
$a3=ord getc file1;
$a4=ord getc file1;
$a5=ord getc file1;
$a6=ord getc file1;
$a7=ord getc file1;
$a8=ord getc file1;$a=$a1.$a2.$a3.$a4.$a5.$a6.$a7.$a8;
print "Number of read $i deccode $a bincode ". bin2dec($a)." char ". chr(bin2dec($a)). "\n";
$i++; }sub bin2dec {
return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
};=================================================
Number of read 1676 deccode 01011000 bincode 88 char X
Number of read 1677 deccode 11100011 bincode 227 char у
Number of read 1678 deccode 01101111 bincode 111 char o
Number of read 1679 deccode 10010001 bincode 145 char С
Number of read 1680 deccode 01101011 bincode 107 char k
Number of read 1681 deccode 00110011 bincode 51 char 3
>[оверквотинг удален]
> Number of read 1677 deccode 11100011 bincode 227
> char у
> Number of read 1678 deccode 01101111 bincode 111
> char o
> Number of read 1679 deccode 10010001 bincode 145
> char С
> Number of read 1680 deccode 01101011 bincode 107
> char k
> Number of read 1681 deccode 00110011 bincode 51
> char 3Больше похоже на правду, но с условием задачи не сходится. Такой же результат получается если считывать по 8 байт read-ом. Сравнила запуском своего кода, потом запуском Вашего по последним 10 значениям полученным.... Видимо проблема в самом файле а не в том как его считывать. Ну или в логике задичи что-то не то. Спасибо :)
>[оверквотинг удален]
>> char С
>> Number of read 1680 deccode 01101011 bincode 107
>> char k
>> Number of read 1681 deccode 00110011 bincode 51
>> char 3
> Больше похоже на правду, но с условием задачи не сходится. Такой же
> результат получается если считывать по 8 байт read-ом. Сравнила запуском своего
> кода, потом запуском Вашего по последним 10 значениям полученным.... Видимо проблема
> в самом файле а не в том как его считывать. Ну
> или в логике задичи что-то не то. Спасибо :)В вашем файле с данными - находятся нолики и единички.
Каждый нолик и единичка в вашем файле - это один символ представленный естественно одним БАЙТом.Вот первые 32 СИМВОЛА (байта!!!) в вашем файле:
10010100110010001101011101110110Считывая файл побайтно (sic!!!)- мы получим:
1 это первый считанный байт, числовое значение 1 символ в кодировке ASCII "рожица"
0 это ВТОРОЙ считанный БАЙТ, числовое значение 0 символ в кодировке ASCII отсутствует
0 это Третий считанный БАЙТ, числовое значение 0 символ в кодировке ASCII отсутствует
1 и так далее...
0
1
0
и т.д..Исходя из устойчивого желания считать из этого потока символов именно один байт и отвергая предложенные решения, напрашивается мысль, что нужно считывать эти данные группами по 8 считая что 8 считанных символов - это байт в двоичном представлении, и тогда преобразуя полученные 8 символов (нолики и единички) в числовую форму мы получим некий байт...который потом можно преобразовать в символ в какой-то кодировке.
И вот тут есть ньюанс.... если кодировка в которой как мы думаем закодирован этот екст существенно не ASCII- то возможно нужно считывать не группами по 8, а группами по 6 или по 7 .. в зависимости от кодировки...
> open(file1, "text.txt");
> binmode(file1);
> $i=1;
> while (($n = read file1, @dimens1[$i], 1) !=0) {@dimens1[$i] — это срез массива. i-й элемент обозначается $dimens1[$i]. Хотя я так и не понял, зачем здесь вообще массив, он же дальше не используется.
> @dimens2[$i] = bin2dec (@dimens1[$i]);
bin2dec — пример из документации на pack, преобразует строку символов "0" и "1" в число. Откуда здесь строка таких символов?
> print "Bytes readed $n Number of read $i deccode @dimens2[$i] bincode " . dec2bin(@dimens2[$i])."\n";
> $i++;
> };
> die();die — это для аварийного завершения. Здесь подразумевается нормальное.
> sub bin2dec {
> return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
> };
> sub dec2bin {
> my $str = unpack("B32", pack("N", shift));
> $str =~ s/^0+(?=d)//; # otherwise you'll get leading zeros
> return $str;
> };Если я правильно понял суть задачи, то она _очень_ простая, а вы пока что занимаетесь созданием искусственных сложностей и их последующим преодолением. Советую на время забыть про pack/unpack.
>> open(file1, "text.txt");
>> binmode(file1);
>> $i=1;
>> while (($n = read file1, @dimens1[$i], 1) !=0) {
>
>@dimens1[$i] — это срез массива. i-й элемент обозначается $dimens1[$i]. Хотя я так и не >понял, зачем здесь вообще массив, он же дальше не используется.Тут я ошиблась. :) Ну оно работало и ладно. Зачем использовать массив я написала ранее.
>> @dimens2[$i] = bin2dec (@dimens1[$i]);
>bin2dec — пример из документации на pack, преобразует строку символов "0" и "1" в число. >Откуда здесь строка таких символов?Ну по моим проверкам преобразует оно всё равно правильно даже из строки которая содержит бинарные данные. Сама удивилась, перепроверила руками. :)
>> print "Bytes readed $n Number of read $i deccode @dimens2[$i] bincode " . dec2bin(@dimens2[$i])."\n";
>> $i++;
>> };
>> die();
>die — это для аварийного завершения. Здесь подразумевается нормальное.Привычка :)
>Если я правильно понял суть задачи, то она _очень_ простая, а вы пока что занимаетесь >созданием искусственных сложностей и их последующим преодолением. Советую на время >забыть про pack/unpack.
Полный текст задачи (пока что интересует только преобразование файла в массив состоящий из считанных байт, переведенных в 10тичную систему:
От источника А к приёмнику В неким устройством было передано сообщение (файл text.txt прилагается). Текст данного сообщения – это литературный текст русского языка, без каких-либо специальных символов, то есть русские буквы, цифры и знаки препинания. Человек, набиравший текст, возможно допустил некоторое количество ошибок (не слишком много).
Предполагается, что передатчик не обладает абсолютной надежностью, поэтому для обеспечения качества передачи был использован следующий приём – каждый символ был передан пакетом из трёх символов. Например символ А был передан так ААА. Смысл данного приёма заключается в том, что вероятность порчи сразу двух символов меньше, чем одного.
При таком способе передачи верным считается символ, встречающийся в пакете не менее 2-х раз. То есть необходимо учесть, что некоторое искажение при передаче имело место.
Устройство передачи является двоичным устройством. Это означает, что каждый символ передаётся в виде 8-значного двоичного числа.
Аппарат, передающий и принимающий сообщение, в качестве кодировочной таблицы использует таблицу ASCII. А какую таблицу использует устройство на котором был набран текст, неизвестно. Известно только то, что исходная таблица кодирует такой же набор символов, как и таблица ASCII, и кодировка в ней также однозначна (один символ – один код). Коды начинаются с 1.
Поэтому если полученный набор двоичных чисел преобразовать в соответствии с таблице ASCII, то осмысленного текста не получится. Кроме того, в результате технического сбоя в исходном тесте были уничтожены все пробелы.Единственное, что у меня складывается ощющение, что создатели задачи перемудрили что-то с файлом...
Я, кажется, понял, с чем у вас проблема :-) Не бывает байтов в десятичной, двоичной и вообще какой-либо системе счисления. Бывают просто байты, т. е. куски информации, кодирующие одно состояние из 256 возможных. Система счисления — это вопрос интерпретации этой информации как числа. А можно интерпретировать как символ или элемент некого абстрактного множества — на здоровье! Главное, что независимо от интерпретации байты можно сравнивать на «равно — не равно».Итак, у нас есть входной поток байтов. Дёргаем из него подряд три байта. Если среди них есть два (как минимум) одинаковых, то их значение отправляем в выходной поток, иначе сообщаем о невозможности прочитать очередной символ. Переходим к следующим трём байтам. И так, пока не кончится входной поток.
Всё. Осталось предыдущий абзац перевести с русского на пёрл :-) Заметьте, я ни разу там не упомянул систему счисления — она не нужна.
P.S. Непонятен выбор языка. Пёрл не очень-то приспособлен для работы с нетекстовыми данными. Имхо, даже на чистом Си решение вышло бы красивее.
>[оверквотинг удален]
> интерпретировать как символ или элемент некого абстрактного множества — на здоровье!
> Главное, что независимо от интерпретации байты можно сравнивать на «равно —
> не равно».
> Итак, у нас есть входной поток байтов. Дёргаем из него подряд три
> байта. Если среди них есть два (как минимум) одинаковых, то их
> значение отправляем в выходной поток, иначе сообщаем о невозможности прочитать очередной
> символ. Переходим к следующим трём байтам. И так, пока не кончится
> входной поток.
> Всё. Осталось предыдущий абзац перевести с русского на пёрл :-) Заметьте, я
> ни разу там не упомянул систему счисления — она не нужна.А в них нет совпадений если побайтово почему-то... Я начала делать какой-то вывод и перевод между системами счисления.... что бы вообще визуально посмотреть что получилось а не только мордашки в выводе. Думала, что неправильно считываю. Но видимо, не в этом дело.... Спасибо за попытки мне помочь, пойду дальше ломать голову над этим файлом. :)))
> P.S. Непонятен выбор языка. Пёрл не очень-то приспособлен для работы с нетекстовыми
> данными. Имхо, даже на чистом Си решение вышло бы красивее.Ну выбор языка был почти случайным. На сях я никогда не писала раньше, перл хотя бы немного использовала. Просто старые версии vb, qb, турбо паскаля работать под виндой отказались, а ставить на виртуалку что-то ещё кроме подопытной фряхи жутко не хотелось. В итоге пошла по методу наименьшего сопротивления.
Решение на Си действительно оказалось простым и изящным:
#include <stdio.h>
#include <stdlib.h>int nextbit () {
int c = getchar();
if (c == EOF) exit(0);
return c & 1;
}int main (int argc, char *argv[]) {
int a, b, c, i;
while (! feof(stdin)) {
a = 0; b = 0; c = 0;
for (i = 0; i < 8; i++) a |= nextbit() << i;
for (i = 0; i < 8; i++) b |= nextbit() << i;
for (i = 0; i < 8; i++) c |= nextbit() << i;
putchar(a & b | (a | b) & c);
}
}
Читаем блоками по 24 байта, каждый блок должен дать один байт на выходе.Каждая тройка байт (24 бита) определяет 1 или 0. Посчитаем в каждой тройке количество бит =1. Если их меньше 2, значит закодирован 0, иначе 1. Проверку выполняем 8 раз, получая вектор вида 0,1,0,0,1,...
Вектор объединяем в строку вида "01001...", строку упаковываем в байт, байт выводим.Пишем это на Perl, читать придётся от конца к началу:
#!/usr/bin/perl
use warnings;
use strict;my $buf;
open(FH,$ARGV[0]);
print pack("B8",join('',map $_ < 2 ? 0 : 1, unpack("(%B24)8", $buf))) while(read(FH,$buf,24)==24);Чтобы получить частотный словарь, напиши что-нибудь вроде $freq{$sym}++
Файл, по-моему, не к этой задаче. Он содержит только байты 0x00 и 0x01, что не соответствует задаче. Но даже если предположить, что это биты, то необходимый результат не получается -- простой проход по данному файлу следующим php-скриптом (что было под рукой):
<?phpassert_options(ASSERT_BAIL, 1);
$fh = fopen('text.txt', 'rb');
$chars = array();
while (!feof($fh)) {
$bits = fread($fh, 8);
if (strlen($bits) == 0) {
break;
}
assert(strlen($bits) == 8);
$byte = 0;
for ($i = 0; $i < 8; ++$i) {
$byte <<= 1;
$byte |= ord($bits[$i]);
}
$chars[] = $byte;
}
assert(sizeof($chars) % 3 == 0);printf('encoded: %d' . "\n", sizeof($chars));
printf('decoded: %d' . "\n", sizeof($chars) / 3);
for ($i = 0; $i < sizeof($chars) / 3; ++$i) {
$i0 = $chars[$i] == $chars[$i + 1] || $chars[$i] == $chars[$i + 2];
$i1 = $chars[$i + 1] == $chars[$i + 2];
if ($i0 || $i1) {
printf('%d: %d' . "\n", $i, $chars[$i * 3 + ($i0 ? 0 : 1)]);
}
}?>
Выдает следующий результат:
encoded: 46398
decoded: 15466
139: 141
146: 81
366: 89
367: 242
626: 101
...
Таким образом в начале файла повторяющиеся данные отсутствуют до 139 * 3 байта.
>[оверквотинг удален]
> decoded: 15466
> 139: 141
> 146: 81
> 366: 89
> 367: 242
> 626: 101
> ...
>
Ну как раз к этому я тоже пришла. Думала, что ошибка где-то в моей логике. Но файл к этой задаче. Наверное там оно как-то более замудрено. Попробую ещё поковырять. 8ми байтами на 1 символ тоже считать мне не удалось....
Хм, товарищ выше дал мне имею попробовать вначале выбирать один из трех битов, а потом уже их складывать в байты. Не факт, что такой подход правильный, т.к. понятно что из 3 бит хоть два будут одинаковыми, но может именно этого и хотели добиться авторы задачи. Текстовый результат через chr() все-равно абракадаброй, но с ним по крайней мере можно начать работать дальше :).
Скрипт:
<?phpassert_options(ASSERT_BAIL, 1);
$fh = fopen('text.txt', 'rb');
$chars = array();
while (!feof($fh)) {
$bits = fread($fh, 8 * 3);
if (strlen($bits) == 0) {
break;
}
assert(strlen($bits) == 8 * 3);
$byte = 0;
for ($i = 0; $i < 8; ++$i) {
$i0 = $bits[$i * 3] == $bits[$i * 3 + 1] || $bits[$i * 3 ] == $bits[$i * 3 + 2];
$i1 = $bits[$i * 3 + 1] == $bits[$i * 3 + 2];
assert($i0 || $i1);
$byte <<= 1;
$byte |= ord($bits[$i * 3 + ($i0 ? 0 : 1)]);
}
$chars[] = $byte;
}printf('chars: %d' . "\n", sizeof($chars));
print_r(array_slice($chars, 0, 32));?>
>[оверквотинг удален]
> $byte <<= 1;
> $byte |= ord($bits[$i * 3 + ($i0 ? 0 :
> 1)]);
> }
> $chars[] = $byte;
> }
> printf('chars: %d' . "\n", sizeof($chars));
> print_r(array_slice($chars, 0, 32));
> ?>
>Спасибо огромное. Кажется, действительно, то, что нужно. Дописала пару строк в Ваше решение, теперь оно в конце считает количество вхождений каждого кода. Получилось, что всего используется чуть больше 70 кодов, похоже, действительно, на русский язык. Дальше уже сама разберусь.