C#:在二进制文件中读写列表 [英] C#: Writing and Reading List to and from Binary File

查看:77
本文介绍了C#:在二进制文件中读写列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个具有User类的程序,该类保存到文件中以存储用户信息.但是,可以有多个不同的用户,因此,不是将文件另存为单个用户,而是一个用户列表.这是XNA中的游戏,因此是游戏,但这实际上并不会对事物产生任何影响.用户拥有硬币,皮肤,库存,用户名,密码(如果有)和hasPassword布尔值.因为用户有硬币,所以我不希望它是可编辑的文本文件,所以我将其保存为二进制文件.我编写了一些代码,对其进行了测试,并且一切正常,直到我尝试将多个用户保存到列表中.由于某种原因,每当我保存第二个用户并读入列表时,列表中只有一个对象(第一个用户).在这里,我将提供代码和示例以使其更易于理解:

I wrote a program that has a User class, which is saved to file to store user information. However, there can be multiple different users, so instead of saving the file as a single User, it's a List of Users. This is in XNA, and therefore a game, but this shouldn't really have any affect on things. Users have Coins, Skins, their Inventory, their Username, their Password(if they have one), and a hasPassword bool. Because users have coins, I don't want this to be an editable text file, so I saved it as a binary file. I wrote some code, tested it, and everything was fine and dandy, until I tried to save multiple users to the List. For some reason, whenever I saved a second user and read in the List, the List only had one object (the first User). Here, I'll provide the code and an example to make this easier to understand:

[Serializable]
class User
{
    #region Fields & Properties

    public string Username = "";
    public string Password = "";
    public bool HasPassword = false;

    public int Coins = 0;
    public List<Achievement> AchievementsCompleted = new List<Achievement>();
    public List<InventoryItem> Inventory = new List<InventoryItem>();
    public List<string> Skins = new List<string>();

    public string CurrentSkinAsset { get; set; }

    const int SPACING = 10;

    const string FILE_PATH = "Users.bin";

    #endregion

    #region Constructors

    public User(string username, int coins, string skinPath, ContentManager content)
    {
        Username = username;
        CurrentSkinAsset = skinPath;
    }
    public User(string username, string password, int coins, string skinPath, ContentManager content) 
        : this(username, coins, skinPath, content)
    {
        HasPassword = true;
        Password = password;
    }

    #endregion

    #region Public Methods

    public static List<User> LoadUsers()
    {
        FileStream fileStream = null;
        List<User> returnList = null;
        try
        {
            if (File.Exists(FILE_PATH))
            {
                fileStream = new FileStream(FILE_PATH, FileMode.Open, FileAccess.Read);
                BinaryFormatter deserializer = new BinaryFormatter();
                returnList = (List<User>)deserializer.Deserialize(fileStream);
            }
            else
            {
                fileStream = File.Create(FILE_PATH);
                List<User> users = new List<User>();
                BinaryFormatter serializer = new BinaryFormatter();
                serializer.Serialize(fileStream, users);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("There's been an error. Here is the message: " + e.Message);
        }
        finally
        {
            if (fileStream != null)
            {
                fileStream.Close();
            }
        }

        return returnList;
    }

    public void SerializeUser()
    {
        // Also works
        FileStream fileStream = null;
        List<User> users;
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        try
        {
            if (File.Exists(FILE_PATH))
            {
                fileStream = File.Open(FILE_PATH, FileMode.Open);
                //fileStream = new FileStream(FILE_PATH, FileMode.Open, FileAccess.ReadWrite);
                users = (List<User>)binaryFormatter.Deserialize(fileStream);
            }
            else
            {
                fileStream = File.Create(FILE_PATH);
                users = new List<User>();
            }

            for (int i = 0; i < users.Count; i++)
            {
                if (users[i].Username.ToLower() == this.Username.ToLower())
                {
                    users.Remove(users[i]);
                }
            }
            users.Add(this);
            binaryFormatter.Serialize(fileStream, users);
        }
        catch (Exception e)
        {
            Console.WriteLine("There's been an error. Here is the message: " + e.Message);
        }
        finally
        {
            if (fileStream != null)
            {
                fileStream.Close();
            }
        }
    }

    #endregion
}

这是我编写的测试代码(在Game1 LoadContent方法中):

Here is the test code I wrote (in the Game1 LoadContent method):

User test = new User("Bob", 0, "skin1", Content)
test.SerializeUser();

我在Debug文件夹中找到了一个名为"Users.bin"的新文件.如预期的那样,它具有一堆随机字符,等等.我将测试的用户名参数从"Bob"更改为"Joe",这应该添加一个新用户.但是...

I looked in the Debug folder and found a new file called "Users.bin". It had a bunch of random characters and such, as is expected. I changed test's username parameter from "Bob" to "Joe", which should add a new User. But...

以下是读取用户的代码:

Here's the code to read the users:

List<User> testList = User.LoadUsers();

我在下一行代码中插入了一个断点,并将鼠标悬停在testList上,但是唯一可见的用户是Bob,而Joe不存在.有任何想法吗? 另外:我十三岁.我可能不太了解您使用的所有术语,因为我已经有很长时间没有编程了. 预先感谢!

I inserted a breakpoint at the next line of code, and hovered over testList, but the only visible user was Bob, and Joe didn't exist. Any ideas? Also: I'm thirteen. I may not understand all the terms you use, as I haven't been programming for a super long time. Thanks in advance!

推荐答案

问题是,当您保存用户列表时,您正在打开FileStream,读取列表(以替换当前用户),并且然后将新列表保存到FileStream ,而无需重置位置.初始读取将FileStream推进到文件末尾-保存然后将新列表附加到初始列表之后的FileStream上.

The problem is that when you save your list of users, you are opening the FileStream, reading in the list (to replace the current user), and then saving the new list to the FileStream without resetting the position. The initial read advances the FileStream to the end of the file- the save then appends the new list to the FileStream after the initial list.

然后发生的是您有一个看起来像这样的文件:

What happens then is you have a file that looks like this:

*start of file*
List(User1)
List(User1,User2)
*end of file*

每次阅读时,您只会得到该初始列表.

and every time you read it, you are only getting that initial list.

尝试一下:

        users.Add(this);
        filStream.Position = 0;
        binaryFormatter.Serialize(fileStream, users);

对于它的价值,这是一种处理保存和加载用户的非常效率低下的方法.实质上,您是在为任何用户的任何更改读取和写入整个列表.它也不是线程安全的(这意味着如果这是一个具有多个同时更新的多人游戏,则根据打开方式和更新内容的不同,用户文件中可能会损坏).您可能想研究一个合适的数据库,而不是一个普通的文件.

For what it's worth, this is a very inefficient way to deal with saving and loading your users. You are essentially reading and writing the whole list for every change to any user. It's also not thread safe (meaning if this is a multiplayer game with multiple simultaneous updates, you might get corruption in your users file depending on how you open it and what updates happen). You might want to look into a proper database rather than a plain file.

这篇关于C#:在二进制文件中读写列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆