代码高尔夫 - 十六进制到(原始)二进制转换

时间:2009-04-27 19:53:20

标签: binary hex code-golf

在回答this question询问十六进制到(原始)二进制转换时,一条评论表明它可以用“5-10行C或任何其他语言”来解决。

我确信(某些)脚本语言可以实现,并希望了解如何。对于C,我们能否证明评论是真的吗?

注意:这并不意味着十六进制到 ASCII 二进制 - 特别是输出应该是与输入ASCII十六进制对应的原始八位字节流。此外,输入解析器应跳过/忽略空格。

编辑(作者:Brian Campbell)为了保持一致性,我是否可以提出以下规则?如果您认为这些内容没有帮助,请随意编辑或删除这些内容,但我认为由于对某些案例应如何运作进行了一些讨论,因此有些说明会有所帮助。

  1. 程序必须从stdin读取并写入stdout(我们也可以允许读取和写入在命令行上传入的文件,但我无法想象在任何语言中都会比stdin和stdout更短)< / LI>
  2. 程序必须仅使用基础标准语言发行版附带的软件包。对于C / C ++,这意味着它们各自的标准库,而不是POSIX。
  3. 程序必须编译或运行时不会将任何特殊选项传递给编译器或解释器(因此,'gcc myprog.c'或'python myprog.py'或'ruby myprog.rb'都可以,而'ruby -rscanf不允许使用myprog.rb';要求/导入模块会计入您的字符数。)
  4. 程序应该读取由相邻的十六进制数字对(大写,小写或混合大小写)表示的整数字节,可选地由空格分隔,并将相应的字节写入输出。每对十六进制数字首先写入最重要的半字节。
  5. 程序对无效输入的行为(除了[a-fA-F \t\r\n]之外的字符,将单个字节中的两个字符分开的空格,输入中的奇数个十六进制数字)是未定义的;任何行为(除了主动损坏用户的计算机或其他东西)都是可接受的(抛出错误,停止输出,忽略坏字符,将单个字符视为一个字节的值,都可以)
  6. 程序可能不会写任何额外的字节来输出。
  7. 代码按源文件中最少的总字节数进行评分。 (或者,如果我们想要对原始挑战更加真实,那么得分将基于最低代码行数;在这种情况下,我会对每行施加80个字符的限制,否则你会得到一堆1线的关系)。

16 个答案:

答案 0 :(得分:8)

编辑 Checkers已将我的C解决方案缩减为46 bytes,由于BillyONeal的提示加上我的错误修正(不再有无限循环),因此该解决方案减少到44个字节输入,现在它只是终止循环)。请将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个字节或两行代码(如果可以将#include放在同一行上,则为1)。请注意,这在输入错误时会有无限循环;在Checkers和BillyONeal的帮助下,这个44字节的解决方案修复了这个错误,并且只是在错误的输入上停止。

#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

答案 1 :(得分:7)

39 char perliner

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

编辑:并不是真的接受大写,已修复。

答案 2 :(得分:7)

45字节可执行文件(base64编码):

6BQAitjoDwDA4AQI2LQCitDNIevrWMOy/7QGzSF09jLkBMAa5YDkByrEJA/D

(粘贴到扩展名为.com的文件中)

编辑:好的,这是代码。打开一个Window的控制台,创建一个名为'hex.com'的45字节文件,输入“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

按回车键,'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

这很棘手。我不敢相信我花时间这样做。

答案 3 :(得分:6)

由于C在功能原型方面的宽大,

Brian's 77-byte C solution可以改进到44个字节。

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

答案 4 :(得分:4)

在Python中:

binary = binascii.unhexlify(hex_str)

一线! (是的,这是作弊。)

答案 5 :(得分:3)

编辑:此代码是在问题编辑之前很长一段时间编写的,它充实了要求。

鉴于单行C可以包含大量语句,所以几乎可以肯定没有用。

在C#中,我几乎可以肯定地用超过10行编写它,即使它在10中是 possible 。我将“解析nybble”部分从“转换为a”中分离出来。字符串到字节数组“part。

当然,如果你不关心发现不正确的长度等,它会变得容易一些。您的原始文本还包含空格 - 是否应跳过,验证等?它们是必需输入格式的一部分吗?

我更倾向于怀疑评论是在没有考虑到什么是令人愉快的,可读的解决方案的情况下做出的。

话虽如此,这是C#中一个可怕的版本。对于奖励积分,它完全不恰当地使用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这个词,这总是一个奖励。 )

答案 6 :(得分:2)

尔加。

我不允许按照袖手旁观估计给我打电话! ;-P

这是一个没有奇怪格式的9行C版本(好吧,我会授予你,将tohex阵列更好地分成16行,这样你就可以看到哪些字符代码映射到哪些值...),并且只有除了一次性脚本之外我不会部署的2个快捷方式:

#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);
}

没有组合的行(每个语句都有自己的行),它是完全可读的等等。模糊的版本无疑可能更短,可以作弊并将紧密括号放在与前面的语句相同的行等等,等等等等。

我不喜欢的两件事是我在那里没有close(fd),main不应该是void并且应该返回一个int。可以说它们不是必需的 - 操作系统将释放程序使用的每个资源,文件将关闭而没有任何问题,编译器将处理程序退出值。鉴于它是一次性使用脚本,它是可以接受的,但不要部署它。

两者都变成十一行,所以它不是一个巨大的增长,十行版本将包括一个或另一个,取决于哪个人可能觉得是两个邪恶的出租人。

它没有进行任何错误检查,并且它不允许空格 - 再次假设它是一次性程序,然后在运行脚本之前执行搜索/替换并删除空格和其他空格更快但是,它也不需要超过另外几行来吃白色空间。

当然,有一些方法可以缩短它,但它们可能会显着降低可读性......

哼。 只需阅读有关行长的评论,所以这里是一个较新的版本,带有一个更丑陋的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循环,因此我不必为i + = 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个字符,但是3级三元宏可以替换前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);}}

而且,再一次,进一步的混淆和比特琐事可能会导致一个更短的例子。

答案 7 :(得分:2)

的Perl

当然,在一条(相当短的)线上:

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

答案 8 :(得分:2)

哈斯克尔:

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

答案 9 :(得分:2)

.

它的语言叫做“Hex!”。它唯一的用法是从stdin读取十六进制数据并将其输出到stdout。 十六进制!由一个简单的python脚本解析。 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"

答案 10 :(得分:1)

相当可读的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;
}

答案 11 :(得分:1)

一个31个字符的Perl解决方案:

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

答案 12 :(得分:0)

我无法将其编码到我的头顶,但是对于每两个字符,输出(字节)((AsciiValueChar1-(AsciiValueChar1&gt; 64?48:55)* 16)+(AsciiValueChar1-(AsciiValueChar1&gt; 64) ?48:55)))将十六进制字符串更改为原始二进制文件。如果你的输入字符串有0到9或A到B之外的任何东西,这会破坏性,所以我不能说它对你有多大用处。

答案 13 :(得分:0)

我知道Jon已经发布了一个(更干净的)LINQ解决方案。但是,有一次我能够使用LINQ语句在执行期间修改字符串并滥用LINQ的延迟评估而不会被我的同事大吼大叫。 :P

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();

还有一个声明。可以通过在开头的十六进制字符串上运行replace(“”,“”)来缩短范围,但这将是第二个语句。

这个有两点有趣。如何在没有源字符串本身以外的外部变量的帮助下跟踪字符数。在解决这个问题时,我遇到了这样的事实:char y.CompareTo(x)只返回“y - x”而int y.CompareTo(x)返回-1,0或1.所以char y.CompareTo(x).CompareTo(0 )等于char比较,返回-1,0或1。

答案 14 :(得分:0)

PHP ,28个符号:

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

答案 15 :(得分:0)

游戏后期,但这里有一些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())]))
相关问题