如何在C#中的字节数组中找到字节模式?

时间:2012-07-07 21:16:23

标签: c# arrays search byte

我正在开展一个项目,我从文件中读取数据,我需要操作一些数据。

数据是二进制文件,其中包含一些ASCII编码文本。 如果重要的话,数据也会保存为Big Endian。

我想要完成的是在此数据中找到一个模式并操纵该模式的一部分。

示例:(09 49 6E 76 65 6E 74 6F 72 79 0A 00 00 00 02 01 00)

这表示库存中有多少个字母的数字9,后面是没有引号的ASCII“库存”。 “0A”标记该ASCII文本的结尾,然后标记00 00 00 02以标记我们的库存“2”的大小。 “01 00”标记整个库存区域的结束。

示例2:(04 53 6C 6F 74 00 02 00)

这表示插槽中有多少个字母的数字4,后面是没有引号的ASCII“插槽”。 “00”是ASCII文本和插槽号“02”之间的空格,后跟区域“00”的结尾。

我需要在文件中找到这些模式和其他几个模式。 然后我需要修改模式的一部分并写入磁盘。

示例修改:(04 53 6C 6F 74 00 02 00)“从上方”到(04 53 6C 6F 74 00 07 00)将“插槽编号'02'更改为'07'。

另一个警告是,虽然我需要在包含不同大小,长度和数据的文件中搜索多个模式,但是这些模式的多个部分可能包含需要单独修改的不同数据。一个整体。

澄清:(库存,槽位,身份证,计数) - 将被视为一个人的信息。

每个人都可以有多个(库存,老虎机,内容,计数)副本。

我想向用户显示此信息,并为他们提供修改组中每个元素的选项。

我不是一个优秀的程序员,想要学习,如果你有例子我很欣赏,如果你有建议,请给它。如果你能把它愚蠢到更好,谢谢。 我现在正在进行一项工作,但现在我被困住了。如果你想看看我有什么,请告诉我。

我的总结:将文件读入byte []然后将整个数组显示到控制台。就是这样。稍加一些格式化和一些调试信息,以便找到我读入数组的那个块。

这是我在pastebin上的代码的链接。 LINK

我意识到我没有得到所有(Inventory,Slot,id,Count)我也需要修复它。

编辑:示例(09 49 6E 76 65 6E 74 6F 72 79 0A 00 00 00 02 01 00)这是我正在读取的文件中的固定长度的二进制数据块。如上所述,09表示弦的长度。字符串后面的内容是(0A 00 00 00 02 01 00),其重要部分是(02) 这是因为那是在那片二进制数据中唯一改变的字节。 “02”表示“2”表示该特定人员记录有2个(Slot,id,Count)实例。

(File)
    (09 Inventory 0A 00 00 00 02 01 00) // Start of person 1's record with 2 instances.
        (Slot)
        (id)
        (Count)
        (Slot)
        (id)
        (Count)
    (Rotation)  // End of person 1's record

    (09 Inventory 0A 00 00 00 04 01 00) // Start of person 2's record with 4 instances.
        (Slot)
        (id)
        (Count)
        (Slot)
        (id)
        (Count)
        (Slot)
        (id)
        (Count)
        (Slot)
        (id)
        (Count)
    (Rotation)  // End of person 2's record
(File End)

我的想法是,我想通过添加到库存来编辑“id”或增加插槽数量,并添加更多的实例(Slot,id,Count)。

“id” - 包含商品ID

“Slot” - 包含库存槽号

“计数” - 包含该插槽中有多少项目。 编辑注:如果我不清楚,请告诉我,再次感谢你。

2 个答案:

答案 0 :(得分:3)

你说你不是一个优秀的程序员。我会提出一些基本的建议。

首先,对于任何问题,请将其分解。尝试将其分解为您可以解决的更简单的子问题,或者认为更容易解决。你说你想检查文件中的模式,然后可能修改一些数据并将其写回。作为第一步,我会说这些是您需要解决的主要子问题才能获得完整的解决方案。

  • 阅读文件
  • 搜索二进制数据中的模式
  • 进行修改
  • 将修改后的数据写出来。

您主要关注“搜索模式”部分,因此我将重点关注这一点。所有其他部分也可能很棘手,但您可以自己解决它们,或者在stackoverflow上寻找提示,或者在其他地方查找其他人如何解决它们。

好的,现在,关于“搜索模式”。在我阅读你的描述时,

在我看来,字节数组中的第一个字节是文本长度。在C#代码中,可以显式声明展示您的模式的数据数组:

byte[] data = new byte[] { 9, 0x49, 0x6E, 0x76, 0x65, 0x6E, 0x74,
               0x6F, 0x72, 0x79, 0x0A, 0, 0, 0, 2, 1, 0 } ;

当然,您不会明确声明该数组。您将通过文件读取操作创建或填充数组。此时文件读取仍然是一个开放的问题,但没关系 - 你可以单独解决它。并且,效果是一样的 - 当您成功读取文件时,您将拥有一个类似于上面显式声明的数组的字节数组。

好的,现在你如何操纵这些数据?第一个字节是字符串数据的长度。接下来的N个字节是字符串数据。然后你还有其他一些东西。

您没有准确描述您想要制作的“修改”,但是根据您提供的信息,您可以轻松浏览数据。

如果要获取数组中数据的“切片”,可以执行此操作。

int length = (int) data[0];
byte[] s = new byte[length];
Array.Copy(data, 0, s, 0, length);
// now, s contains N bytes, representing the string

您可能想要做的下一件事是从您切出的那些字节中获取实际字符串。为此,请使用the Encoding class

String word = System.Text.Encoding.UTF8.GetString(s);

我不理解字符串数据后面的数据模式。在某些情况下,它是00,在某些情况下是0A

但也许你知道这种模式。按照相同的方式进行操作,您可以浏览一个“记录”的所有数据。当你到达记录的末尾,然后再次开始并处理下一个。进入下一个“记录”的关键是知道从文件中读取的数据中取出的数据量有多大。


这使我们回到“阅读文件”子问题。从文件中获取适量数据的一种方法是只读取第一个字节,然后在文件中进一步读取N个字节。 (见System.IO.Stream.Read)。

byte[] size = new byte[1];
var fileStream = File.OpenRead(path);
int offset = 0;
int n;
n = fileStream.Read(size, offset, 1);
// size[0] now contains the byte of data indicating the number of bytes to follow

此时您可以读取下一个N个字节:

int length = (int)size[0];
byte[] stringData = new byte[length];
n = fileStream.Read(stringData, offset, length);

现在你可以得到字符串了。

String word = System.Text.Encoding.UTF8.GetString(stringData);

......等等。读取所有尾随字节。此时,fileStream隐式保留的光标指向文件中的下一条记录。

答案 1 :(得分:0)

我同意@Cheeso上面所说的,这是一个好的开始。我将描述一种替代解决方案:

  1. 定义所有已知部分的操作码(一个以0x4 0x56开头,一个以0xA 0x2开头等)。
  2. 在内存中读取文件。
  3. 在内存中的字节数组上应用匹配算法,并提取每个匹配模式的起始位置。出于性能原因,我建议使用一种众所周知的搜索算法(或其变体),例如Knuth-Morris-PrattBoyer-Moore
  4. 此时您应该在应用程序中缓存您找到的所有模式的所有起始位置。你可以使用这个列表的列表(其中键是模式类型),但你可以将它包含到一个自定义类(文件中具有模式ID和模式偏移),然后只使用一个列表并使用LINQ访问所有内容。
  5. 在需要时访问和解码实际模式,因此在您阅读文件后不会立即访问和解码。我想你对它们有不同的用途,并不是所有人在加载文件后都会出现。
  6. 因此,这为您提供了文件中所有模式出现的列表。您可以使用它向用户显示某种列表。

    我快速查看了您的代码。它真的很大,全都塞满了几个大功能。你需要稍微构建它。每个任务需要一个类来加载和解析文件,然后是许多较小的函数。

    当您在应用程序中使用模式时,例如当您将其提供给用户进行修改时,使用此机制应该相当容易:

    • 以您已有的偏移量读取数据。
    • 你知道偏移,你也知道长度。当回写修改时,您可以将自己定位在该特定位置的加载流中并覆盖。

    必须经常处理文件中的数据以进行各种修改我会说最好将它保存在内存中并在需要时保存它,仍然将修改后的版本保留在内存中。有时可能需要在内存缓存中更新模式偏移,因此您只能将初始解析过程应用于文件的子部分(以加快过程)。

    如果在加载文件后立即需要模式数据,则可以在第一次遍历文件时(提取偏移量时)应用模式解码过程。我建议只保留一个单独的集合中的重要内容,以便向用户显示(例如只需要ID和您需要显示的文本)。在您真正需要时加载整个条目。