【问题标题】:Adding strings from Unreal Engine 4 binary save file to list/array将 Unreal Engine 4 二进制保存文件中的字符串添加到列表/数组
【发布时间】:2021-05-28 22:18:46
【问题描述】:

我正在尝试读取以二进制形式保存的游戏保存文件。具体来说,我想将所有完整字符串分别添加到数组中。我目前有这个代码:

using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (BinaryReader reader = new BinaryReader(stream))
    {
        while (reader.BaseStream.Position != reader.BaseStream.Length)
        {
            Console.WriteLine(reader.ReadString() + "\n");
        }
    }
}

但是,可以理解的是,这是胡言乱语,因为我相信我的 while 循环一次推进 1 个字节。我怎样才能可靠地找到保存文件中的每个“实际”单词?作为参考,这是我要在十六进制编辑器中查看的信息:image。

另一个解决方案是遍历文件中的所有字节并以这种方式读取字符:

StringBuilder sBuilder = new StringBuilder();
bool inBuildState = false;
foreach (byte currByte in File.ReadAllBytes(path)) {
    bool isChar = currByte >= 0x20 && currByte <= 0x7F;

    if (inBuildState && !isChar) {
        Console.WriteLine(sBuilder.ToString() + "\n");
        sBuilder.Clear();
    }

    inBuildState = isChar;
    if (inBuildState) sBuilder.Append((char)currByte);
}

这个解决方案效果很好,实际上并准确地返回了我需要的字符串。非常感谢@Flydog57。

【问题讨论】:

  • 你的字符串是如何编码的?各种语言(比如西方、中东和东方语言)的混合很难与二进制数据区分开来。如果您的字符串都是带有空终止符的 7 位 ASCII,那么它们非常容易区分
  • 这对我来说很难说(因为我不太了解)。 Here's another snippet 的纯文本文件。它看起来不是很有序(它是由虚幻引擎 4 生成的)。
  • 逐字节循环。如果字节在 0x20-0x7f 范围内,则它可能是一个字符。您的程序应该有两种状态,是否在字符串中。如果您不在字符串中并且看到可能的字符,请转换为“在字符串中”并使用 StringBuilder 来构建字符串。当您看到更多可能的字符时,附加到 StringBuilder。当您看到“非字符”时,将 StringBuilder 转换为字符串并将其添加到 Word 列表中(清除 StringBuilder 以备下次使用)
  • 如果您希望有人提出一个可行的解决方案,请将您的一堆二进制数据作为十六进制流发布(就像您在第一张图片中一样:00 43 68 65 etc,但作为文本)。
  • 您对文件中的字节进行迭代的想法非常有效。我已经编辑了我的帖子以显示我正在使用的代码。

标签: c# stream binaryfiles unreal-engine4


【解决方案1】:

你不能。

BinaryReader API 的工作方式是它可以读取您编写的文件,或者您知道符合特定格式的文件。在 ReadString() 的情况下,它读取第一个字节以获得后续字符串的长度,如果最高有效位为 1,则长度在下一个字节继续,依此类推。之后,它会根据当前的编码读取一定数量的字节,然后将这些字节解码成一个字符串。

但是您拥有的文件不是由 BinaryWriter 写入的,因此 BinaryReader 无法读取它。您的文件以 00 开头,因此表示字符串长度为 0。

您需要自定义代码来读取此文件中的字节并将字节解码为字符串。如果您忽略00 00 0x 00 序列,会显示所有字符串都以00 开头。

【讨论】:

    【解决方案2】:

    这可能是我的用例独有的,但这是我最终解决问题的方式;由于虚幻引擎 4 生成的二进制文件在很多地方都是乱码,因此不能解释为纯文本。所以我必须先把它变成这样。
    为此,我使用了正则表达式模式,将不属于其中的任何字符替换为空格,从而形成一行包含大量空格的长行。然后,我使用 LINQ 在这些空格上拆分字符串并忽略所有空结果(空格)。这给我留下了所有真实字符串/单词的IEnumerable&lt;string&gt;,我将其转换为List&lt;string&gt;(因为这是我需要的)。

    这是我的代码:

    // Regular expression containing a-z, A-Z, 0-9, -, _, and space.
    Regex regex = new Regex("[^a-zA-Z0-9 -_]");
    
    // Reading the file and immediately replacing all characters not part of the regex with spaces.
    string fileToPlainText = regex.Replace(File.ReadAllText(filePath), " ");
    
    // Creating a list of all items that aren't empty strings after splitting the string on spaces.
    List<string> plainTextToList = fileToPlainText.Split(' ').Where(x => !String.IsNullOrEmpty(x)).ToList();
    

    所需的using 指令如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text.RegularExpressions;
    

    【讨论】:

    • 既然您说这是“希望更快/更少资源消耗”,您可能希望在使用正则表达式和蛮力遍历每个字符之间进行一次烘焙,然后做一个“这个字符是在 0x20 和 0x7f 之间吗”测试(每次读取同一个文件数千次,并使用秒表来计时结果)。我愿意赌 0.25 美元,蛮力解决方案更快。但是,如果你所拥有的足够好,那么,你知道,它已经足够好了:)
    猜你喜欢
    • 2013-10-24
    • 1970-01-01
    • 2019-06-26
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 2020-12-29
    • 2013-09-24
    相关资源
    最近更新 更多