【问题标题】:Help with byte shifting帮助字节移位
【发布时间】:2009-05-15 03:33:45
【问题描述】:

我需要对文本文件进行字节移位。我对 perl 一无所知,但我在 perl 中发现了一段完美的代码,称为 moz-byteshift.pl (documentation)。这正是我想做的,但我需要在 C# 中完成。

这里是perl文件的源代码:

#!/usr/bin/perl

# To perform a byteshift of 7
#   To decode: moz-byteshift.pl -s -7 <infile >outfile
#   To encode: moz-byteshift.pl -s  7 <infile >outfile

# To perform a byteshift of 13
#   To decode: moz-byteshift.pl -s -13 <infile >outfile
#   To encode: moz-byteshift.pl -s  13 <infile >outfile

use encoding 'latin1';
use strict;
use Getopt::Std;

use vars qw/$opt_s/;

getopts("s:");
if(!defined $opt_s) {
  die "Missing shift\n";
}

my $buffer;
while(1) {
  binmode(STDIN, ":raw");
  my $n=sysread STDIN, $buffer, 1;
  if($n == 0) {
    last;
  }
  my $byte = unpack("c", $buffer);
  $byte += 512 + $opt_s;
  $buffer = pack("c", $byte);
  binmode(STDOUT, ":raw");
  syswrite STDOUT, $buffer, 1;
}

如果有人至少能解释一下 perl 脚本是如何工作的,那就太好了。 C# 中等效的示例代码会更好。 =)

感谢您的帮助。

【问题讨论】:

  • 我不明白。如果,正如您在一条评论中所说,您实际上并不知道 perl 脚本的作用,那么您怎么知道这是您想要做的?
  • 一个同事正在使用这个脚本来执行我现在必须实现的功能。就是这样。

标签: c# perl byte-shifting


【解决方案1】:

没什么好说的。它一次读取一个文件,将每个字节的值调整为任意值(通过 -s 标志指定),然后写出调整后的字节。它是文本文件的 ROT-13 加密的二进制等价物。

其余的细节是特定于 Perl 如何做这些事情的。 getopts() 是一个处理命令行开关的函数(来自 Getopt::Std 模块)。 binmode() 将文件句柄置于原始模式以绕过 Perl 通常在 I/O 期间执行的任何魔法。 sysread() 和 syswrite() 函数用于低级流访问。 pack() 和 unpack() 函数用于读写二进制数据; Perl 不做原生类型。

在 C 中重新实现这将是微不足道的。我建议这样做(并在需要时从 C# 绑定到它)而不是直接移植到 C#。

【讨论】:

  • 谢谢。这很有帮助。我想我不明白的部分是它做了什么类型的转变。它是否需要一个像这样的字节数组:byte[] {1,2,3,4,5} 并且(移位一个)产生这个:byte[] {5,1,2,3,4}?还是移动每个字节的位,将:byte[]{00000001,00000010,00000011} 转换为(移位一):byte[] {10000000,00000001,10000001}?
  • 将此称为“转变”有点用词不当。它不移动位或字节。它将偏移量应用于每个字节的值。如果您的原始数据的字节值为 1、2、3,并且您指定“-s 5”,则结果将为 6、7、8。
  • 所以它增加了字节值?那么移位为 1,00000001 变为 00000010,00001000 变为 00001001,等等?
  • @Andrew:没错。另请注意,这些值环绕。即 0xFE + 0x04 = 0x02。这使得转换是可逆的。
【解决方案2】:

代码的作用是:从标准输入中逐个读取每个字节(在将其切换到原始模式后,不会发生转换)。解包获取刚刚读取的字符的字节值,因此“0”读取变为 0x30。选择了 latin1 编码,以便这种转换是一致的(例如,参见 http://www.cs.tut.fi/~jkorpela/latin9.html)。

然后将在命令行中使用 -s 选项指定的值与 512 一起添加到该字节,以模拟模运算。这样,-s 0、-s 256 等是等效的。我不知道为什么需要这样做,因为我会假设下面的包已经解决了这个问题,但我认为他们一定有充分的理由把它放在那里。

然后,将原始字节写入标准输入。

当您在包含字符 012345 的文件上运行它时会发生以下情况(我将数据放在 DATA 部分):

E:\Test> byteshift.pl -s 1 | xxd
0000000: 3132 3334 3536 0b                        123456.

每个字节值加一。

E:\Test> byteshift.pl -s 257 | xxd
0000000: 3132 3334 3536 0b                        123456.

记住 257 % 256 = 1。即:

$byte += $opt_s;
$byte %= 256;

相当于代码中使用的单步。

很久以后:好吧,我不懂 C#,但这是我能够使用在线文档拼凑而成的。懂 C# 的人应该解决这个问题:

using System;
using System.IO;

class BinaryRW {
    static void Main(string[] args) {
        BinaryWriter binWriter = new BinaryWriter(
                Console.OpenStandardOutput()
                );
        BinaryReader binReader = new BinaryReader(
                Console.OpenStandardInput()
                );

        int delta;

        if ( args.Length < 1 
                || ! int.TryParse( args[0], out delta ) )
        {
            Console.WriteLine(
                    "Provide a non-negative delta on the command line"
                    );
        } 
        else {       
            try  {
                while ( true ) {
                    int bin = binReader.ReadByte();
                    byte bout = (byte) ( ( bin + delta ) % 256 );
                    binWriter.Write( bout );
                }
            }

            catch(EndOfStreamException) { }

            catch(ObjectDisposedException) { }

            catch(IOException e) {
                Console.WriteLine( e );        
            }

            finally {
                binWriter.Close();
                binReader.Close();

            }
        }
    }
}

E:\Test> xxd bin
0000000: 3031 3233 3435 0d0a 0d0a                 012345....

E:\Test> b 0 < bin | xxd
0000000: 3031 3233 3435 0d0a 0d0a                 012345....

E:\Test> b 32 < bin | xxd
0000000: 5051 5253 5455 2d2a 2d2a                 PQRSTU-*-*

E:\Test> b 257 < bin | xxd
0000000: 3132 3334 3536 0e0b 0e0b                 123456....

【讨论】:

  • 我认为 512 应该是强制值换行而不是饱和的偏差。不过,我认为没有必要(至少在 Perl 中没有)。
  • 谢谢!这完美无缺。我不会从命令行使用它,但对于其他发现此问题的人,您的代码中有一个错误:您应该在 if 条件的开头添加 args.Length &lt; 1 || 以避免“索引超出没有输入任何内容时出现 bounds" 异常。
  • 为什么要捕获 delta
  • 我猜只是精神错乱。我专注于使语法正确,以便程序能够编译。
【解决方案3】:

从其他答案来看,C# 中的等价物看起来像这样:

using(Stream sIn = new FileStream(inPath))
{
  using(Stream sOut = new FileStream(outPath))
  {
    int b = sIn.ReadByte();
    while(b >= 0)
    {
      b = (byte)b+1; // or some other value
      sOut.WriteByte((byte)b);
      b = sIn.ReadByte();
    }
    sOut.Close();
  }
  sIn.Close();
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2019-04-14
  • 2010-09-07
  • 1970-01-01
  • 1970-01-01
  • 2010-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多