想问种类型的写法,没学过想不出来

runerback 2016-04-30 02:38:54
有种文件结构,暂且称之为 stf 文件
我想用类或者结构来表示这种文件的结构,但贴具体的图上来感觉不太好,所以举个栗子。

假设开头是 “PCF” 即

data[0] = 0x50;
data[1] = 0x43
data[2] = 0x46
data[3] = 0x00;

接着有两个字节用来表示宽度信息

//width = 512
data[4] = 0x00;
data[5] = 0x02

两个字节表示高度信息

//height = 1024
data[6] = 0x00;
data[7] = 0x04

假设 8 到 16 这段内容需要根据前面的高宽来确定,然后 17 到 1024 这段用 0 填充,
后面的是长度不固定的具体数据信息。

我遇到的问题是,不知道该怎么表示。
想用类来表示,但写出来就成了这样:

public class STF
{
byte[] data1 = new byte[4]
{
80, 67, 70, 0
};

int width { get; set; }
int height { get; set; }

byte[] data2
{
get
{
byte[] data = System.BitConverter.GetBytes(this.width)
.Take(2).Reverse().ToArray();
return data;
}
}

byte[] data3
{
get
{
byte[] data = System.BitConverter.GetBytes(this.height)
.Take(2).Reverse().ToArray();
return data;
}
}

byte[] data4 = new byte[8]
{
0, 1, 2, 3, 4, 5, 6, 7//... ...
};

byte[] data5 = new byte[]
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0//... ...
};

byte[] data6;

//... ...
}

不知道我有没有把这种别扭的感觉表达清楚

struct 好像可以表示这种结构,但我不知道该怎么写才能让 struct 像数组一样直接用下标访问。

我不是计算机专业的没学过怎么表示这种结构。
路过的大神帮忙看看啊
...全文
316 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
runerback 2016-05-02
  • 打赏
  • 举报
回复
貌似 byte[] 的序号必须是 4 的倍数,是不是要凑成一个 int32 啊?
runerback 2016-05-01
  • 打赏
  • 举报
回复
用结构体写固定部分,数组存放不固定的部分,然后把这两个放进类中,问题基本是解决了。 下午那会测第一种类型都没问题了,开始写第二种文件类型,也测试通过了。 结果这会写了一些方法之后,第一种类型死活通不过,问题也很诡异:

        [FieldOffset(0)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        private byte[] title;

        [FieldOffset(4)]
        public Int2 data1;
就是data1那里,不管我 title 用上面这种还是就一个 byte,下面的 data1 必须从大于 4 的位置开始,不然就报错。我就纳闷了,真的 比如我改成 3:
引用
……because it contains an object field at offset 3 that is incorrectly aligned or overlapped by a non-object field.
问题确定不在 Int2 这个上

    [StructLayout(LayoutKind.Explicit, Size = 2)]
    public struct Int2
因为我写了个新的结构体,同样的错误

    [StructLayout(LayoutKind.Explicit, Size = 10)]
    public struct Header
    {
        [FieldOffset(0)]
        private byte header;

        [FieldOffset(3)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] width;
    }
但我改成这样就又没错误了:

    [StructLayout(LayoutKind.Explicit, Size = 10)]
    public struct Header
    {
        [FieldOffset(0)]
        private byte header;

        [FieldOffset(3)]
        public byte width;
    }
实在不解……
runerback 2016-05-01
  • 打赏
  • 举报
回复
引用 5 楼 shaoerbao 的回复:
[quote=引用 1 楼 shingoscar 的回复:]
然后不都写完了吗!

不别扭么。。我反编译看了一个dll,里面用下标直接访问,感觉整个结构体就是一个数组,不知道怎么弄[/quote]
我记错了,刚看了下,它里面是用指针操作的
runerback 2016-05-01
  • 打赏
  • 举报
回复
引用 3 楼 caojinrong 的回复:
                if (index < pos + datai.Length)
                {
                    datai[index - pos] = value;
                    return;
                }
用 this 索引不错,比较接近我想实现的效果
runerback 2016-05-01
  • 打赏
  • 举报
回复
引用 1 楼 shingoscar 的回复:
然后不都写完了吗!
不别扭么。。我反编译看了一个dll,里面用下标直接访问,感觉整个结构体就是一个数组,不知道怎么弄
xuzuning 2016-05-01
  • 打赏
  • 举报
回复
        public class STF
{
byte[] data = new byte[1024];
public string Type
{
get { return Encoding.ASCII.GetString(data.Where((x, index) => index < 3).ToArray()); }
}
public int Width
{
get { return BitConverter.ToInt16(data, 4); }
set
{
var v = BitConverter.GetBytes((short)value);
v.CopyTo(data, 4);
}
}
public int Height
{
get { return BitConverter.ToInt16(data, 6); }
set
{
var v = BitConverter.GetBytes((short)value);
v.CopyTo(data, 6);
}
}
public STF(string str)
{
data = Encoding.Default.GetBytes(str);
}
}

所有的操作,都是只对缓冲区 data 进行的
而你 #10 的方案就缺少这个缓冲区
            var p = new STF("PCF\x00\x00\x02\x00\x04");
Console.WriteLine(p.Type);
Console.WriteLine(p.Width);
Console.WriteLine(p.Height);

p.Width = 300;
p.Height = 600;
Console.WriteLine(p.Type);
Console.WriteLine(p.Width);
Console.WriteLine(p.Height);
Console.ReadKey();
runerback 2016-05-01
  • 打赏
  • 举报
回复
我的意思是从文件中读取或写入文件有没有现成的方法,自己写的话麻烦点
runerback 2016-05-01
  • 打赏
  • 举报
回复
有个问题就是,如果空着的部分怎样在写入文件的时候自动补上0,比如:

    [StructLayout(LayoutKind.Explicit, Size = 1024)]
    public struct Header
    {
        [FieldOffset(0)]
        private byte[] data0;//size = 4

        [FieldOffset(12)]
        private byte[] data3;//size = 4
    }
第5到12位补0,16位之后全部为0. 真感觉C#里的结构体就是个败笔
runerback 2016-05-01
  • 打赏
  • 举报
回复
引用 13 楼 xuzuning 的回复:
除非还需要重新写回文件
确实需要。 我要做的是,从一种类型的文件头中读取该文件包含的信息以及固定偏移量之后的数据,然后根据其头部的信息来创建另一种类型的文件头,再把数据放在后面,生成新文件。两种文件相互生成。 具体点就是实现游戏中的贴图文件与DDS格式文件相互转换…… 我是想最好能有办法在保存成文件的时候按顺序写入字节,但不知道怎么样写方便点,同时便于别人查看。之前写过了,但实现很笨拙,自己都几乎看不懂。
xuzuning 2016-05-01
  • 打赏
  • 举报
回复
其实所有字段都没有 set 的。 因为你的数据是从文件读取的,应一次性赋值 除非还需要重新写回文件 但如果还需要写回文件的话,一个一个字段写,也是很麻烦的
xuzuning 2016-05-01
  • 打赏
  • 举报
回复
也可以这样
        public class STF
{
byte[] data = new byte[1024];
public string Type
{
get { return Encoding.ASCII.GetString(data.Where((x, index) => index < 3).ToArray()); }
}
public int Width
{
get { return BitConverter.ToInt16(data, 4); }
}
public int Height
{
get { return BitConverter.ToInt16(data, 6); }
}
public STF(string str)
{
data = Encoding.Default.GetBytes(str);
}
}

static void Main(string[] args)
{
var p = new STF("PCF\x00\x00\x02\x00\x04");
Console.WriteLine(p.Type);
Console.WriteLine(p.Width);
Console.WriteLine(p.Height);
Console.ReadKey();
}
runerback 2016-05-01
  • 打赏
  • 举报
回复
但还是得给每个字节都安个名字……看来没其他办法了。
runerback 2016-05-01
  • 打赏
  • 举报
回复
this[offset] 这样写应该可以,所以我又写了个通用的方法来 get/set:

public class Offset<StructType> where StructType : struct
{
public static object GetValue(StructType item, int offset)
{
try
{
var field = GetFieldAtOffset(offset);
return field.GetValue(item);
}
catch
{
throw;
}
}

public static void SetValue(ref StructType item, int offset, object value)
{
try
{
var field = GetFieldAtOffset(offset);
field.SetValueDirect(__makeref(item), value);
}
catch
{
throw;
}
}

private static FieldInfo GetFieldAtOffset(int offset)
{
try
{
Type structType = typeof(StructType);
var result = structType
.GetFields((BindingFlags)52)
.FirstOrDefault(field =>
{
var attr = field.GetCustomAttributes(
typeof(FieldOffsetAttribute), false)
.FirstOrDefault();
if (attr != null)
{
var offsetAttr = attr as FieldOffsetAttribute;
return offsetAttr.Value == offset;
}
return false;
});
if (result == null)
{
string exc = string.Format("Can not found field at offset {0}, Type is {1}", offset, structType.FullName);
throw new ArgumentException(exc);
}
return result;
}
catch
{
throw;
}
}
}


然后在结构体中加上this索引

[StructLayout(LayoutKind.Explicit)]
public struct Header
{
[FieldOffset(0)]
private byte data0;

[FieldOffset(1)]
private byte data1;

[FieldOffset(2)]
private byte data2;

[FieldOffset(3)]
private byte data3;

public byte this[int offset]
{
get
{
object value = Offset<Header>.GetValue(this, offset);
return (byte)value;
}
set
{
Offset<Header>.SetValue(ref this, offset, value);
}
}
}


测试:

public class Tester
{
public static void Do1()
{
Header data = new Header();
Console.WriteLine("value of offset 0: {0}", data[0]);
Console.WriteLine("value of offset 0: {0}", data[1]);
Console.WriteLine("value of offset 0: {0}", data[2]);
Console.WriteLine("value of offset 0: {0}", data[3]);
Console.WriteLine("---------------------");
data[0] = 1;
data[1] = 11;
data[2] = 111;
data[3] = 255;
Console.WriteLine("value of offset 0: {0}", data[0]);
Console.WriteLine("value of offset 0: {0}", data[1]);
Console.WriteLine("value of offset 0: {0}", data[2]);
Console.WriteLine("value of offset 0: {0}", data[3]);
}
}




这样爽多啦
runerback 2016-05-01
  • 打赏
  • 举报
回复
引用 8 楼 shingoscar 的回复:
c#里就没有这样的语法
因为是C++.Net……
Poopaye 2016-05-01
  • 打赏
  • 举报
回复
引用 7 楼 shaoerbao 的回复:
[quote=引用 5 楼 shaoerbao 的回复:] [quote=引用 1 楼 shingoscar 的回复:] 然后不都写完了吗!
不别扭么。。我反编译看了一个dll,里面用下标直接访问,感觉整个结构体就是一个数组,不知道怎么弄[/quote] 我记错了,刚看了下,它里面是用指针操作的 [/quote] 这偏的有点厉害啊 还有,c#里就没有这样的语法
devmiao 2016-04-30
  • 打赏
  • 举报
回复
用结构体,结构体里可以嵌套结构体。
caojinrong 2016-04-30
  • 打赏
  • 举报
回复
                if (index < pos + datai.Length)
                {
                    datai[index - pos] = value;
                    return;
                }
caojinrong 2016-04-30
  • 打赏
  • 举报
回复
可以按这个思路改,但这绝不是最优解
public class STF
{
    byte[] data1 = new byte[4] { 80, 67, 70, 0 };
    byte[] data2 = new byte[2];
    byte[] data3 = new byte[2];
    byte[] data4 = new byte[8]
    {
            0, 1, 2, 3, 4, 5, 6, 7//... ...
    };

    byte[] data5 = new byte[]
    {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0//... ...
    };

    byte[] data6;

    //... ...

    int width { get { return (int)(data2[0] << 8) + data2[1]; } set { data2[0] = (byte)(value >> 8); data2[1] = (byte)value; } }
    int height { get { return (int)(data3[0] << 8) + data3[1]; } set { data3[0] = (byte)(value >> 8); data3[1] = (byte)value; } }

    public byte this[int index]
    {
        get
        {
            int pos = 0;                //0
            for (int i = 0; i < 6; i++) //从data1至data6
            {
                byte[] datai = GetType().GetField("data" + i, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(this) as byte[];
                if (datai == null)
                    continue;
                if (index < pos + datai.Length)
                    return datai[index - pos];

                pos += datai.Length;
            }

            throw new IndexOutOfRangeException();
        }
        set
        {
            int pos = 0;                //0
            for (int i = 0; i < 6; i++) //从data1至data6
            {
                byte[] datai = GetType().GetField("data" + i, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(this) as byte[];
                if (datai == null)
                    continue;
                if (index < pos + datai.Length)
                    datai[index - pos] = value;

                pos += datai.Length;
            }

            throw new IndexOutOfRangeException();
        }
    }
}
Poopaye 2016-04-30
  • 打赏
  • 举报
回复
然后不都写完了吗!

110,538

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧