Подтвердить что ты не робот

Code golf - шестнадцатеричное (необработанное) двоичное преобразование

В ответ на этот вопрос, в котором говорится о шестнадцатеричном (необработанном) двоичном преобразовании, комментарий предположил, что он может быть разрешен в "5-10 строках C" или любой другой язык ".

Я уверен, что для (некоторых) языков сценариев, которые могут быть достигнуты, и хотелось бы посмотреть, как это сделать. Можем ли мы доказать, что комментарий верно для C тоже?

NB: это не означает, что hex - это двоичный код ASCII - в частности, выход должен быть необработанным октетом, соответствующим входному шестнадцатеричному ASCII. Кроме того, входной парсер должен пропускать/игнорировать пробелы.

edit (Брайан Кэмпбелл) Могу ли я предложить следующие правила для согласованности? Не стесняйтесь редактировать или удалять их, если вы не считаете их полезными, но я думаю, что, поскольку было некоторое обсуждение того, как некоторые случаи должны работать, некоторые разъяснения будут полезны.

  • Программа должна читать из stdin и записывать в stdout (мы также можем разрешать чтение и запись файлов, передаваемых в командной строке, но я не могу себе представить, что это было бы короче на любом языке, чем stdin и stdout)
  • Программа должна использовать только пакеты, включенные в базу, стандартное языковое распространение. В случае C/С++ это означает, что их соответствующие стандартные библиотеки, а не POSIX.
  • Программа должна компилироваться или запускаться без каких-либо специальных опций, переданных компилятору или интерпретатору (так что "gcc myprog.c" или "python myprog.py" или "ruby myprog.rb" в порядке, а "ruby -rscanf myprog.rb 'не допускается, требуя/импортируя модули, подсчитывается против вашего количества символов).
  • Программа должна читать целочисленные байты, представленные парами соседних шестнадцатеричных цифр (верхний, нижний или смешанный регистр), необязательно разделенные пробелами и записывать соответствующие байты в вывод. Каждая пара шестнадцатеричных цифр записывается с наиболее значительным первым делом.
  • Поведение программы на недопустимом входе (символы помимо [a-fA-F \t\r\n], пробелы, разделяющие два символа в отдельном байте, нечетное число шестнадцатеричных цифр на входе) undefined; любое поведение (отличное от активного повреждения компьютера пользователя или что-то еще) при плохом вводе допустимо (бросание ошибки, остановка вывода, игнорирование плохих символов, обработка одиночного символа как значения одного байта, все в порядке)
  • Программа не может писать никаких дополнительных байтов для вывода.
  • Код забивается наименьшим количеством байтов в исходном файле. (Или, если бы мы хотели быть более верными исходной проблеме, оценка была бы основана на самом низком числе строк кода, я бы наложил ограничение на 80 символов на строку в этом случае, так как в противном случае вы получили бы кучу связей для 1 линии).
4b9b3361

Ответ 1

edit Шашки сократили мое решение C до 46 байт, которое затем было уменьшено до 44 байтов благодаря подсказке от BillyONeal плюс bugfix с моей стороны (не более бесконечный цикл при неудачном вводе, теперь он просто завершает цикл). Пожалуйста, отдайте кредит Checkers за сокращение этого с 77 до 46 байт:

main(i){while(scanf("%2x",&i)>0)putchar(i);}

И у меня гораздо лучшее решение Ruby, чем у моего последнего, в 42 38 байт (спасибо Joshua Swank за предложение regexp):

STDIN.read.scan(/\S\S/){|x|putc x.hex}

оригинальные решения

C, в 77 байт или две строки кода (будет 1, если вы можете поместить #include в ту же строку). Обратите внимание, что у этого есть бесконечный цикл на плохом входе; 44-байтное решение с помощью Checkers и BillyONeal исправляет ошибку и просто останавливается на плохом вводе.

#include <stdio.h>
int main(){char c;while(scanf("%2x",&c)!=EOF)putchar(c);}

Это даже 6 строк, если вы отформатируете его в обычном режиме:

#include <stdio.h>
int main() {
  char c;
  while (scanf("%2x",&c) != EOF)
    putchar(c);
}

Ruby, 79 байт (я уверен, что это можно улучшить):

STDOUT.write STDIN.read.scan(/[^\s]\s*[^\s]\s*/).map{|x|x.to_i(16)}.pack("c*")

Они берут ввод от STDIN и записывают в STDOUT

Ответ 2

39 char perl oneliner

y/A-Fa-f0-9//dc,print pack"H*",$_ for<>

Изменить: на самом деле не принимал заглавные, исправленные.

Ответ 3

45-байтовый исполняемый файл (закодированный base64):

6BQAitjoDwDA4AQI2LQCitDNIevrWMOy/7QGzSF09jLkBMAa5YDkByrEJA/D

(вставить в файл с расширением .com)

EDIT: Хорошо, вот код. Откройте консоль Windows, создайте файл с 45 байтами с именем hex.com, введите "debug hex.com", затем "a" и введите. Скопируйте и вставьте эти строки:

db e8,14,00,8a,d8,e8,0f,00,c0,e0,04,08,d8,b4,02,8a,d0,cd,21,eb,eb,cd,20
db b2,ff,b4,06,cd,21,74,f6,32,e4,04,c0,1a,e5,80,e4,07,2a,c4,24,0f,c3

Нажмите enter, 'w', а затем введите снова, 'q' и введите. Теперь вы можете запустить 'hex.com'

EDIT2: Сделано на два байта меньше!

db e8, 11, 00, 8a, d8, e8, 0c, 00, b4, 02, 02, c0, 67, 8d, 14, c3
db cd, 21, eb, ec, ba, ff, 00, b4, 06, cd, 21, 74, 0c, 04, c0, 18
db ee, 80, e6, 07, 28, f0, 24, 0f, c3, cd, 20

Это было сложно. Я не могу поверить, что я потратил время на это.

Ответ 4

Брайанский 77-байтовый C-решение может быть улучшено до 44 байт, благодаря снисходительности C в отношении прототипов функций.

main(i){while(scanf("%2x",&i)>0)putchar(i);}

Ответ 5

В Python:

binary = binascii.unhexlify(hex_str)

ОДНА ЛИНИЯ! (Да, это обман.)

Ответ 6

EDIT: Этот код был написан долгое время перед редактированием вопроса, в котором были сформулированы требования.

Учитывая, что одна строка C может содержать огромное количество утверждений, она почти наверняка верна, не будучи полезной.

В С# я почти наверняка напишу его более чем в 10 строках, хотя это было бы возможно в 10. Я бы выделил часть "parse nybble" из части "преобразовать строку в массив байтов".

Конечно, если вам не нужно определять неправильные длины и т.д., это становится немного проще. В вашем исходном тексте также содержатся пробелы - должны ли они пропущены, проверены и т.д.? Являются ли они частью требуемого формата ввода?

Я скорее подозреваю, что комментарий был сделан без рассмотрения относительно того, как будет выглядеть приятное, читаемое решение.

Сказав это, здесь отвратительная версия в С#. Для бонусных очков он полностью использует LINQ, чтобы сохранить строку или два кода. Линии могут быть длиннее, конечно...

using System;
using System.Linq;

public class Test
{
    static void Main(string[] args)
    {
        byte[] data = ParseHex(args[0]);
        Console.WriteLine(BitConverter.ToString(data));

    }

    static byte[] ParseHex(string text)
    {
        Func<char, int> parseNybble = c => (c >= '0' && c <= '9') ? c-'0' : char.ToLower(c)-'a'+10;
        return Enumerable.Range(0, text.Length/2)
            .Select(x => (byte) ((parseNybble(text[x*2]) << 4) | parseNybble(text[x*2+1])))
            .ToArray();
    }
}

(Это позволяет избежать "обмана" с помощью любого встроенного шестнадцатеричного кода синтаксического анализа, например Convert.ToByte(string, 16). Помимо всего прочего, это означало бы потерю использования слова nybble, которое всегда является бонусом.)

Ответ 7

Г.

Вам не позволено называть меня моими оценками!;-P

Здесь версия с 9 строками C без нечетного форматирования (ну, я дам вам, что массив tohex лучше разбит на 16 строк, чтобы вы могли видеть, какие кодовые символы сопоставляются с какими значениями...) и только 2 ярлыков, которые я бы не разворачивал ничем, кроме одноразового script:

#include <stdio.h>
char hextonum[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char input[81]="8b1f0008023149f60300f1f375f40c72f77508507676720c560d75f002e5ce000861130200000000";
void main(void){
   int i = 0;
   FILE *fd = fopen("outfile.bin", "wb");
   while((input[i] != 0) && (input[i+1] != 0))
      fputc(hextonum[input[i++]] * 16 + hextonum[input[i++]], fd);
}

Нет комбинированных строк (каждому выражению присваивается собственная строка), он отлично читается и т.д. Запущенная версия, несомненно, может быть короче, можно обмануть и поместить тесные фигурные скобки в ту же строку, что и предыдущий, и т.д. и т.д., и т.д.

Две вещи, которые мне не нравятся, это то, что у меня нет близкого (fd) там, и main не должен быть недействительным и должен возвращать int. Возможно, они не нужны - ОС выпустит каждый ресурс, который использует программа, файл будет закрыт без каких-либо проблем, и компилятор позаботится о выходе из программы. Учитывая, что это одноразовое использование script, оно приемлемо, но не развертывайте это.

Он становится одиннадцатью строками с обоими, поэтому он не является огромным увеличением в любом случае, и десятистрочная версия будет включать в себя тот или иной, в зависимости от того, что может показаться лизингодателем двух зол.

Он не выполняет никаких проверок ошибок и не допускает пробелов - если предположить, что это одноразовая программа, то быстрее выполнять поиск/замену и избавляться от пробелов и пробелов перед запуском script, однако не нужно больше, чем еще несколько строк, чтобы есть пробелы.

Есть, конечно, способы сделать его короче, но они, вероятно, значительно уменьшат читаемость...

Хммм. Просто прочитайте комментарий о длине строки, так что здесь более новая версия с более уродливым макросом hextonum, а не массив:

#include <stdio.h>
#define hextonum(x) (((x)<'A')?((x)-'0'):(((x)<'a')?((x)+10-'A'):((x)+10-'a')))
char input[81]="8b1f0008023149f60300f1f375f40c72f77508507676720c560d75f002e5ce000861130200000000";
void main(void){
   int i = 0;
   FILE *fd = fopen("outfile.bin", "wb");
   for(i=0;(input[i] != 0) && (input[i+1] != 0);i+=2)
      fputc(hextonum(input[i]) * 16 + hextonum(input[i+1]), fd);
}

Это не ужасно нечитаемо, но я знаю, что у многих людей есть проблемы с тройным оператором, но соответствующее присвоение имени макроса и некоторый анализ должны с готовностью давать, как это работает для среднего программиста на C. Из-за побочных эффектов в макросе мне пришлось переходить в цикл for, поэтому мне не нужно было иметь другую строку для я + = 2 (hextonum(i++) будет увеличивать я на 5 каждый раз, когда она вызывается, макро-побочные эффекты не для слабый от сердца!).

Кроме того, анализатор ввода должен пропускать/игнорировать пробелы.

роптать, ворчать, ворчать.

Мне пришлось добавить несколько строк, чтобы позаботиться об этом требовании, теперь до 14 строк для достаточно форматированной версии. Он будет игнорировать все, что не является шестнадцатеричным символом:

#include <stdio.h>
int hextonum[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
char input[]="8b1f 0008 0231 49f6 0300 f1f3 75f4 0c72 f775 0850 7676 720c 560d 75f0 02e5 ce00 0861 1302 0000 0000";
void main(void){
   unsigned char i = 0, nibble = 1, byte = 0;
   FILE *fd = fopen("outfile.bin", "wb");
   for(i=0;input[i] != 0;i++){
      if(hextonum[input[i]] == -1)
         continue;
      byte = (byte << 4) + hextonum[input[i]];
      if((nibble ^= 0x01) == 0x01)
         fputc(byte, fd);
   }
}

Я не беспокоился с длиной строки 80 символов, потому что входной сигнал не менее 80 символов, но трехмерный трехмерный макрос может заменить первый 256-элементный массив. Если кто-то не возражал против "альтернативного форматирования", то следующая 10-строчная версия не полностью нечитаема:

#include <stdio.h>
int hextonum[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
char input[]="8b1f 0008 0231 49f6 0300 f1f3 75f4 0c72 f775 0850 7676 720c 560d 75f0 02e5 ce00 0861 1302 0000 0000";
void main(void){
   unsigned char i = 0, nibble = 1, byte = 0;
   FILE *fd = fopen("outfile.bin", "wb");
   for(i=0;input[i] != 0;i++){
      if(hextonum[input[i]] == -1) continue;
      byte = (byte << 4) + hextonum[input[i]];
      if((nibble ^= 0x01) == 0x01) fputc(byte, fd);}}

И, опять же, дальнейшая обфускация и свертывание бит могут привести к еще более короткому примеру.

Ответ 8

Perl

В, конечно, одна (довольно короткая) строка:

my $bin = map { chr hex } ($hex =~ /\G([0-9a-fA-F]{2})/g);

Ответ 9

Haskell:

import Data.Char
import Numeric
import System.IO
import Foreign

main = hGetContents stdin >>= 
       return.fromHexStr.filter (not.isSpace) >>=  
       mapM_ (writeOneByte stdout)

fromHexStr (a:b:tl) = fromHexDgt [a,b]:fromHexStr tl
fromHexStr [] = []
fromHexDgt str =  case readHex str of 
  [(i,"")] -> fromIntegral (i)
  s -> error$show s

writeOneByte h i = allocaBytes 1 (wob' h i)
wob' :: Handle -> Int8 -> (Ptr Int8) -> IO ()
wob' h i ptr = poke ptr i >> hPutBuf h ptr 1

Ответ 10

.

Его язык называется "Hex!". Его единственное использование - считывать шестнадцатеричные данные из stdin и выводить его на stdout. Hex! анализируется простым python script. import sys

try:
  data = open(sys.argv[1], 'r').read()
except IndexError:
  data = raw_input("hex!> ")
except Exception as e:
  print "Error occurred:",e

if data == ".":
  hex = raw_input()
  print int(hex, 16)
else:
  print "parsing error"

Ответ 11

Довольно читаемо C-решение (9 "реальных" строк):

#include <stdio.h>
int getNextHexDigit() {
    int v;
    while((v = fgetc(stdin)) < '0' && v != -1) {    /* Until non-whitespace or EOF */
    }
    return v > '9' ? 9 + (v & 0x0F) : v - '0';      /* Extract number from hex digit (ASCII) */
}
int main() {
    int v;
    fputc(v = (getNextHexDigit() << 4) | getNextHexDigit(), stdout);
    return v > 0 ? main(0) : 0;
}

Чтобы поддерживать 16-битное малое конечное качество, замените main на:

int main() {
    int v, q;
    v = (getNextHexDigit() << 4) | getNextHexDigit();
    fputc(q = (getNextHexDigit() << 4) | getNextHexDigit(), stdout);
    fputc(v, stdout);
    return (v | q) > 0 ? main(0) : 0;
}

Ответ 12

31-символьное решение Perl:

s/\W//g,print(pack'H*',$_)for<>

Ответ 13

Я не могу кодировать это с головы, но для каждых двух символов вывод (байт) ((AsciiValueChar1- (AsciiValueChar1 > 64? 48: 55) * 16) + (AsciiValueChar1- (AsciiValueChar1 > 64? 48: 55))), чтобы получить шестнадцатеричную строку, замененную на необработанную двоичную. Это ужасно нарушило бы, если бы ваша строка ввода имела что-то отличное от 0 до 9 или от A до B, поэтому я не могу сказать, насколько она вам полезна.

Ответ 14

Я знаю, что Jon уже опубликовал (более чистое) решение LINQ. Но на этот раз я могу использовать оператор LINQ, который изменяет строку во время ее выполнения, и злоупотребляет отложенной оценкой LINQ, не закричавшись моими коллегами.: Р

string hex = "FFA042";
byte[] bytes =
    hex.ToCharArray()
       .Select(c => ('0' <= c && c <= '9') ? 
                         c - '0' :
                         10 + (('a' <= c) ? c - 'a' : c - 'A'))
       .Select(c => (hex = hex.Remove(0, 1)).Length > 0 ? (new int[] {
           c,
           hex.ToCharArray()
                 .Select(c2 => ('0' <= c2 && c2 <= '9') ?
                                    c2 - '0' :
                                    10 + (('a' <= c2) ? c2 - 'a' : c2 - 'A'))
                 .FirstOrDefault() }) : ( new int[] { c } ) )
       .Where(c => (hex.Length % 2) == 1)
       .Select(ca => ((byte)((ca[0] << 4) + ca[1]))).ToArray();

1 форматируется для чтения.

Обновление

Поддержка пространств и неравномерного количества десятичных знаков (89A равна 08 9A)

byte[] bytes =
    hex.ToCharArray()
       .Where(c => c != ' ')
       .Reverse()
       .Select(c => (char)(c2 | 32) % 39 - 9)
       .Select(c => 
           (hex =
                new string('0', 
                           (2 + (hex.Replace(" ", "").Length % 2)) *
                                hex.Replace(" ", "")[0].CompareTo('0')
                                                       .CompareTo(0)) +
                hex.Replace(" ", "").Remove(hex.Replace(" ", "").Length - 1))
              .Length > 0 ? (new int[] {
                        hex.ToCharArray()
                           .Reverse()
                           .Select(c2 => (char)(c2 | 32) % 39 - 9)
                           .FirstOrDefault(), c }) : new int[] { 0, c } )
                     .Where(c => (hex.Length % 2) == 1)
                     .Select(ca => ((byte)((ca[0] << 4) + ca[1])))
                     .Reverse().ToArray();

Еще одно утверждение. Может быть сделано намного короче, заменив ( "," ") на шестую строку в начале, но это будет второй оператор.

Два интересных момента с этим. Как отслеживать количество символов без помощи внешних переменных, отличных от самой исходной строки. При решении этого я столкнулся с тем, что char y.CompareTo(x) просто возвращает "y-x", а int y.CompareTo(x) возвращает -1, 0 или 1. Таким образом, char y.CompareTo(x).CompareTo(0) означает сравнение char, которое возвращает -1, 0 или 1.

Ответ 15

PHP, 28 символов:

<?=pack(I,hexdec($argv[1]));

Ответ 16

Поздно к игре, но здесь несколько Python {2,3} с одним слоем (100 символов, требуется import sys, re):

sys.stdout.write(''.join([chr(int(x,16)) for x in re.findall(r'[A-Fa-f0-9]{2}', sys.stdin.read())]))