C # чтение файла

#c# #io

#c# #io

Вопрос:

У меня есть две функции:

     public void SaveMap()
    {
        StreamWriter stream = new StreamWriter("file.txt");
        for (int j = 0; j < GameConstants.MapMaxTilesX; j  )
        {
            for (int i = 0; i < GameConstants.MapMaxTilesY; i  )
            {
                stream.Write( map[j,i] );
            }
        }
        stream.Close();
    }

    public void LoadMap()
    {
        StreamReader stream = new StreamReader("file.txt");
        for (int j = 0; j < GameConstants.MapMaxTilesX; j  )
        {
            for (int i = 0; i < GameConstants.MapMaxTilesY; i  )
            {
                map[j, i] = (int)stream.Read();
            }
        }
        stream.Close();
    }
  

И поскольку он сохраняет данные должным образом, он их не считывает. Считываются случайные значения (по крайней мере, не те, которые находятся в файле)

Что я сделал не так?

Комментарии:

1. Какие типы содержатся в map[,]? Если вы храните массивы символов, то написанный вами код возвращает не символ, а его ASCII-код.

2. Я воспроизвел проблему, и Stream.Read() возвращает ASCII-код символа, который вы написали ранее, поэтому вам нужно преобразовать его обратно в char и проанализировать как Int32.

Ответ №1:

Несмотря StreamReader.Read() на то, что метод возвращает an int , он не считывает an int из файла. Вместо этого он возвращает один символ. Причина, по которой тип является an int , заключается в том, что он может помечать условие конца файла значением -1. Это пережиток старого стиля ведения дел на C.

Если вы хотите прочитать символы из файла, вы можете вызвать Read() и проверить возвращаемое значение на -1. Если возвращается что-то, отличное от -1, затем преобразуйте это в a char . Тогда вы будете нести ответственность за объединение символов значимым образом. Но есть гораздо более простые способы сделать то, что вы пытаетесь выполнить!

Чтобы сделать то, что вы хотите сделать, вам придется либо каким-то образом разграничить целочисленные значения, а затем проанализировать строки в файле, либо вместо этого использовать BinaryWriter / BinaryReader .

Возможно, самый простой способ сделать это — поместить одно значение в строку следующим образом:

 ...
stream.WriteLine( map[j,i] );
...
map[j, i] = int.Parse(stream.ReadLine());
  

Если вы хотите минимизировать размер файла, я рекомендую записывать каждый int с помощью a BinaryWriter , а затем считывать значения с BinaryReader.ReadInt32() помощью метода. Если вы это сделаете, то файл будет иметь длину ровно 4 * GameConstants.MapMaxTilesX * GameConstants.MapMaxTilesY в байтах.

Еще один интересный способ сделать это — записать каждую строку вашей карты в отдельной строке и отделить каждый столбец символом табуляции 't' . Тогда вы могли бы считывать значения обратно по строке за раз и использовать string.Split для разделения значений в каждой строке. Преимущество этого подхода заключается в том, что вы можете редактировать файл в программе для работы с электронными таблицами или текстовом редакторе.

Комментарии:

1. Я бы определенно выбрал BinaryReader / BinaryWriter вместо StreamReader, если все, что вы храните, — это целые числа.

2. Я тоже согласен, поскольку похоже, что это просто для Int32.

Ответ №2:

При чтении значений из потока байт за байтом Stream.Read() возвращает ASCII-код текущего символа, поэтому сначала вам нужно преобразовать его в обычный символ, а затем проанализировать его как int . Поэтому вместо того, чтобы делать:

 map[j, i] = (int)stream.Read();
  

Вам нужно сделать:

 char c = (char)stream.Read();
map[j, i] = Int32.Parse(c.ToString());
  

И это должно сработать.

Комментарии:

1. StreamReader.Read() фактически возвращает значение кодовой точки в Юникоде прочитанного символа. Он будет возвращать значения ASCII только при чтении чистого файла ASCII, и это только потому, что ASCII является подмножеством Unicode. Документация по этому методу не ясна в этом вопросе, и в примере фактически неправильно используется термин ASCII. Но это определенно возвращаемая кодовая точка Unicode. Например, при чтении файла CP-1252, если встречается символ 151, возвращаемое значение Read() будет равно 8212 (0x2014), потому что это соответствующая кодовая точка Unicode (U 2014 EM DASH ).

2. Нет проблем. Я знал, что вам это понравится! 😛

3. Джеффри неверен. Он возвращает кодовую единицу UTF-16 ( не кодовую точку Unicode) в виде целого числа, чтобы разрешить отображение EOF ( -1 ) . Если бы это было не так, вы бы потеряли приведение информации char , чтобы поместить ее в строку (что является вполне допустимым вариантом использования). Вы можете легко подтвердить это, прочитав кодовую точку, которая представлена в виде двух единиц в UTF-16; например, "𡉕" кодируется как {0xD844, 0xDE55} , и действительно, два Read() вызова дают эту последовательность.