Как читать и обрабатывать двоичные (base-2) логические представления из файла
У меня есть файл, содержащий 800 строк, таких как:
id binary-coded-info
---------------------------
4657 001001101
4789 110111111
etc.
где каждый 0 или 1 обозначает наличие какой-либо функции. Я хочу прочитать этот файл и выполнить несколько побитовых логических операций над двоично-кодированной информацией (операции зависят от ввода пользователя и информации из второго файла с 3000 строками). Затем эти пересчитанные двоичные коды должны быть записаны в файл (с конечными нулями, например,
4657 000110011
4789 110110000
etc.
Как мне сделать это без написания собственной процедуры преобразования базы? Я открыт для всего, также для языков, которые я не знаю, таких как python, perl и т. Д. И это должно работать без компиляции.
До сих пор я пытался написать сценарий, awk и sed мой путь. Это будет означать (я думаю): пакетное чтение как base-2, преобразование в base-10, выполнение побитовых операций в зависимости от пользовательского ввода и второго файла, преобразование в base-2, добавление начальных нулей и печать. Обычные консольные подсказки для использования bc не кажутся изящными, потому что у меня много строк в файле. То же самое относится и к dc.sed. И, похоже, awk не имеет эквивалента для пометки ввода как двоичного (как в "echo $((2#101010)))", а также, трюк printf не работает для двоичного файла. Итак, как бы я сделал это наиболее элегантно (или вообще, в этом отношении)?
6 ответов
Зачем конвертировать их и использовать битовые операции?
В Python вы можете делать все это в виде строки.
for line in myFile:
key, value = line.split()
bits = list(value)
# bits will be a list of 1-char strings ['1','0','1',...]
# ... do stuff to bits ...
print key, "".join( value )
В python вы можете преобразовать в двоичный файл, используя int, указав base 2. т.е.
>>> int('110111111',2)
447
Чтобы преобразовать обратно, есть bin
функция в python2.6 или 3, но не в python2.5, так что вам нужно будет реализовать ее самостоятельно (или использовать что-то подобное ниже):
def bin(x, width):
return ''.join(str((x>>i)&1) for i in xrange(width))[::-1]
>>> bin(447, 9)
110111111
(Ширина - это количество цифр для заполнения - в ваших примерах, похоже, используются 9-битные числа.)
В C вы можете использовать "strtol(str, NULL, 2)" для выполнения преобразования, если вы уже делаете это в C.
Будет работать что-то вроде следующего:
FILE* f = fopen("myfile.txt", "r");
char line[1024];
while ((line = fgets(line, sizeof(line), f))
{
char* p;
long column1 = strtol(line, &p, 10);
long column2 = strtol(p, &p, 2);
...
}
Вам нужно будет добавить обработку ошибок и т. Д.
И, следуя давней традиции, вот версия awk:-)
Последняя проверка на gawk 4.0.1
Должно работать и для других awk.
{
var = _int("00010101",2);
print _bin( or( var , _int("00101001",2) ) , 8 )
print _bin( and( var , _int("10110111",2) ) , 8 )
print _bin( xor( var ,var ) , 8 );
}
# convert var to d-ht base. Specify 16 for hex, 8 for oct, and such. Up to base 36, for fruther base, provide X. if d<=36, and wish to use custom X, provide 1 for i.
function _obase( v , d , X , i, this , r ){
if(d<=9){r="";while(v){r=v%d""r;v=int(v/d)};return r;}
if(d<=36&&!i){for(i=0;i<=9;i++)X[i]=""i;for(;i<d;i++)X[i]=sprintf("%c",55+i);}
r="";while(v){r=X[v%d]""r;v=int(v/d)};return r;
}
function _pad(d, p, w, this ,r){
r=""d;while(length(r)<w)r=p""r;return r;
}
function _bin( v , w , this ){
return _pad(_obase(v,2),"0",w);
}
# convert string to var, using d as base. for base>36, specify X. if wish to use custom X, provide 1 to i
function _int( s , d , X , i , this , k , r ){
r=0;k=length(s);if(d<=9){for(i=1;i<=k;i++){r*=d;r=r+int(substr(s,i,1));}return r;}
if(d<=36&&!i){for(i=0;i<=9;i++)X[""i]=i;for(;i<d;i++)X[sprintf("%c",55+i)]=i;}
for(i=1;i<=k;i++){r*=d;r=r+X[substr(s,i,1)];}eturn r;
}
Функция и () или (), xor() может отсутствовать в некоторых типах awk. Если это так, загрузите библиотеку битов. Есть несколько для awk, плавающих в сети. Или предоставьте свой.
"Простой" Perl one liner (замените foo bar baz quux на ваши флаги
perl -le '@f=qw/foo bar baz quux/;$_&&print($f[$i]),$i++for split//, shift' 1011
Вот читаемая версия Perl:
#!/usr/bin/perl
use strict;
use warnings;
#flags that can be turned on and off, the first
#flag is turned on/off by the left-most bit
my @flags = (
"flag one",
"flag two",
"flag three",
"flag four",
"flag five",
"flag six",
"flag seven",
"flag eight",
);
#turn the command line argument into individual
#ones and zeros
my @bits = split //, shift;
#loop through the bits printing the flag that
#goes with the bit if it is 1
my $i = 0;
for my $bit (@bits) {
if ($bit) {
print "$flags[$i]\n";
}
$i++;
}
Расширяя ответ Брайана:
# Get rid of the '----' line for simplicity
data_file = '''id binary-coded-info
4657 001001101
4789 110111111
'''
import cStringIO
import csv, sys
data = [] # A list for the row dictionaries (we could just as easily have used the regular reader method)
# Read in the file using the csv module
# Each row will be a dictionary with keys corresponding to the first row
reader = csv.DictReader(cStringIO.StringIO(data_file), delimiter=' ', skipinitialspace = True)
try:
for row in reader:
data.append(row) # Add the row dictionary to the data list
except csv.Error, e:
sys.exit('file %s, line %d: %s' % (filename, reader.line_num, e))
# Do something with the bits
first = int(data[0]['binary-coded-info'],2) # First bit string
assert(first & int('00001101',2) == int('1101',2)) # Bitwise AND
assert(first | int('00001101',2) == int('1001101',2)) # Bitwise OR
assert(first ^ int('00001101',2) == int('1000000',2)) # Bitwise XOR
assert(~first == int('110110010',2)) # Binary Ones Complement
assert(first << 2 == int('100110100',2)) # Binary Left Shift
assert(first >> 2 == int('000010011',2)) # Binary Right Shift
См. Документацию по выражениям в python для получения дополнительной информации и модуль csv для получения дополнительной информации о модуле csv.