1 条题解

  • 0
    @ 2025-8-24 22:57:05

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar MrPython
    waac_tXKCMem1dAph0gg0

    搬运于2025-08-24 22:57:05,当前版本为作者最后更新于2024-04-10 12:03:16,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    既然是工程题,那就尽量让程序良构起来吧!

    我们按照手册所说定义两个结构体:

    struct pcap_hdr {
        // 所有字段都是大端序
        uint32_t magic_number;// 用于文件类型识别,始终为 0xA1B2C3D4,
        uint16_t version_major;// 始终为 2
        uint16_t version_minor;// 始终为 4
        int32_t thiszone;// 始终为 0
        uint32_t sigfigs;// 始终为 0
        uint32_t snaplen;// 允许的最大包长度,始终为 262144
        uint32_t network;// 数据类型,本次学习题中始终为 1 (以太网)
    } __attribute__ ((packed));
    struct pcaprec_hdr{
        // 所有字段都是大端序
        uint32_t ts_sec;// 时间戳(秒)
        uint32_t ts_usec;// 时间戳(微秒)
        uint32_t incl_len;// 该片段的存储长度
        uint32_t orig_len;// 该片段实际的长度
    } __attribute__ ((packed));
    

    其中 __attribute__ ((packed)) 是为了避免因内存对齐导致中间出现神秘空位,输入失败。

    我们再将对结构体的输入输出方法定义好:

    friend std::istream& pcap_hdr::operator>>(std::istream& in,pcap_hdr& data){
        in.read((char*)&data,sizeof(pcap_hdr));
        data.magic_number=ntohl(data.magic_number),
        data.version_major=ntohs(data.version_major),
        data.version_minor=ntohs(data.version_minor),
        data.thiszone=ntohl(data.thiszone),
        data.sigfigs=ntohl(data.sigfigs),
        data.snaplen=ntohl(data.snaplen),
        data.network=ntohl(data.network);
        assert(data.magic_number==0xA1B2C3D4&&  // 输入错误导致 RE,方便调试
               data.version_major==2&&
               data.version_minor==4&&
               data.thiszone==0&&
               data.sigfigs==0&&
               data.snaplen==262144&&
               data.network==1
        );
        return in;
    }
    friend std::ostream& operator<<(std::ostream& out,pcap_hdr data){
        data.magic_number=htonl(data.magic_number),
        data.version_major=htons(data.version_major),
        data.version_minor=htons(data.version_minor),
        data.thiszone=htonl(data.thiszone),
        data.sigfigs=htonl(data.sigfigs),
        data.snaplen=htonl(data.snaplen),
        data.network=htonl(data.network);
        out.write((char*)&data,sizeof(pcap_hdr));
        return out;
    }
    friend std::istream& pcaprec_hdr::operator>>(std::istream& in,pcaprec_hdr& data){
        in.read((char*)&data,sizeof(pcaprec_hdr));
        data.ts_sec=ntohl(data.ts_sec),
        data.ts_usec=ntohl(data.ts_usec),
        data.incl_len=ntohl(data.incl_len),
        data.orig_len=ntohl(data.orig_len);
        assert(data.ts_usec<1e6&&data.incl_len==data.orig_len);
        return in;
    }
    friend std::ostream& operator<<(std::ostream& out,pcaprec_hdr data){
        data.incl_len=htonl(data.incl_len),
        data.orig_len=htonl(data.orig_len),
        data.ts_sec=htonl(data.ts_sec),
        data.ts_usec=htonl(data.ts_usec);
        out.write((char*)&data,sizeof(pcaprec_hdr));
        return out;
    }
    

    这里,我使用 std::basic_istream::read 直接将流内的二进制数据写入结构体中,std::basic_ostream::write 将结构体的内容写入流。两个函数都接受一个 const char* 和一个整数,分别表示要写/读的内存地址和要操作的字节数。将指向结构体的类型强制转换,与结构体大小一起传入即可。

    ntohl ntohs htonl htons 定义在 netinet/in.hntohl ntohs 分别将 32 位/16 位大端序整数转换为本机使用的端序,htonl htons 分别将 32 位/16 位本机使用的端序转换为大端序。在输入输出操作前,需要使用这些函数处理端序问题。

    为了好玩 我还定义了两个辅助结构体,分别将报文与文件进行封装。

    struct pcaprec{
        pcaprec_hdr header;
        std::vector<uint8_t> data;
        pcaprec():header(),data(){}
        friend std::istream& operator>>(std::istream& in,pcaprec& data){
            in>>data.header;
            data.data.resize(data.header.orig_len);
            in.read(data.data.data(),data.header.orig_len);
            return in;
        }
        friend std::ostream& operator<<(std::ostream& out,pcaprec const& data){
            out<<data.header;
            out.write((char*)data.data.data(),data.header.incl_len);
            return out;
        }
    };
    struct pcap{
        pcap_hdr header;
        std::vector<pcaprec> data;
        pcap():header(),data(){}
        friend std::istream& operator>>(std::istream& in,pcap& data){
            in>>data.header;
            while (!in.eof()) data.data.push_back([](std::istream& in){
                pcaprec p;in>>p;return p;
            }(in));
            data.data.pop_back();
            return in;
        }
        friend std::ostream& operator<<(std::ostream& out,pcap const& data){
            out<<data.header;
            for (pcaprec const& i:data.data) out<<i;
            return out;
        }
    };
    struct pcap{
        pcap_hdr header;
        std::vector<pcaprec> data;
        pcap():header(),data(){}
        friend std::istream& operator>>(std::istream& in,pcap& data){
            in>>data.header;
            while (!in.eof()) data.data.push_back([](std::istream& in){
                pcaprec p;in>>p;return p;
            }(in));
            data.data.pop_back(); // cin 读取到 eof 后总还要多读一位,只能 pop 掉了
            return in;
        }
        friend std::ostream& operator<<(std::ostream& out,pcap const& data){
            out<<data.header;
            for (pcaprec const& i:data.data) out<<i;
            return out;
        }
    };
    

    做完了这些准备工作,main 函数的内容就相当简单了。

    附上我的代码:

    #ifndef ONLINE_JUDGE
    std::ifstream fin("./data/P10344.in",std::ifstream::binary);
    std::ofstream fout("./data/P10344.out",std::ofstream::binary);
    #else
    std::istream& fin=std::cin;
    std::ostream& fout=std::cout;
    #endif
    struct pcap_hdr {
        // 所有字段都是大端序
        uint32_t magic_number;// 用于文件类型识别,始终为 0xA1B2C3D4,
        uint16_t version_major;// 始终为 2
        uint16_t version_minor;// 始终为 4
        int32_t thiszone;// 始终为 0
        uint32_t sigfigs;// 始终为 0
        uint32_t snaplen;// 允许的最大包长度,始终为 262144
        uint32_t network;// 数据类型,本次学习题中始终为 1 (以太网)
        friend std::istream& operator>>(std::istream& in,pcap_hdr& data){
            in.read((char*)&data,sizeof(pcap_hdr));
            data.magic_number=ntohl(data.magic_number),
            data.version_major=ntohs(data.version_major),
            data.version_minor=ntohs(data.version_minor),
            data.thiszone=ntohl(data.thiszone),
            data.sigfigs=ntohl(data.sigfigs),
            data.snaplen=ntohl(data.snaplen),
            data.network=ntohl(data.network);
            assert(data.magic_number==0xA1B2C3D4&&
                   data.version_major==2&&
                   data.version_minor==4&&
                   data.thiszone==0&&
                   data.sigfigs==0&&
                   data.snaplen==262144&&
                   data.network==1
            );
            return in;
        }
        friend std::ostream& operator<<(std::ostream& out,pcap_hdr data){
            data.magic_number=htonl(data.magic_number),
            data.version_major=htons(data.version_major),
            data.version_minor=htons(data.version_minor),
            data.thiszone=htonl(data.thiszone),
            data.sigfigs=htonl(data.sigfigs),
            data.snaplen=htonl(data.snaplen),
            data.network=htonl(data.network);
            out.write((char*)&data,sizeof(pcap_hdr));
            return out;
        }
    } __attribute__ ((packed));
    struct pcaprec_hdr{
        // 所有字段都是大端序
        uint32_t ts_sec;// 时间戳(秒)
        uint32_t ts_usec;// 时间戳(微秒)
        uint32_t incl_len;// 该片段的存储长度
        uint32_t orig_len;// 该片段实际的长度
        friend std::istream& operator>>(std::istream& in,pcaprec_hdr& data){
            in.read((char*)&data,sizeof(pcaprec_hdr));
            data.ts_sec=ntohl(data.ts_sec),
            data.ts_usec=ntohl(data.ts_usec),
            data.incl_len=ntohl(data.incl_len),
            data.orig_len=ntohl(data.orig_len);
            assert(data.ts_usec<1e6&&data.incl_len==data.orig_len);
            return in;
        }
        friend std::ostream& operator<<(std::ostream& out,pcaprec_hdr data){
            data.incl_len=htonl(data.incl_len),
            data.orig_len=htonl(data.orig_len),
            data.ts_sec=htonl(data.ts_sec),
            data.ts_usec=htonl(data.ts_usec);
            out.write((char*)&data,sizeof(pcaprec_hdr));
            return out;
        }
    } __attribute__ ((packed));
    struct pcaprec{
        pcaprec_hdr header;
        std::vector<uint8_t> data;
        pcaprec():header(),data(){}
        friend std::istream& operator>>(std::istream& in,pcaprec& data){
            in>>data.header;
            data.data.resize(data.header.orig_len);
            in.read((char*)data.data.data(),data.header.orig_len);
            return in;
        }
        friend std::ostream& operator<<(std::ostream& out,pcaprec const& data){
            out<<data.header;
            out.write((char*)data.data.data(),data.header.incl_len);
            return out;
        }
    };
    struct pcap{
        pcap_hdr header;
        std::vector<pcaprec> data;
        pcap():header(),data(){}
        friend std::istream& operator>>(std::istream& in,pcap& data){
            in>>data.header;
            while (!in.eof()) data.data.push_back([](std::istream& in){
                pcaprec p;in>>p;return p;
            }(in));
            data.data.pop_back();
            return in;
        }
        friend std::ostream& operator<<(std::ostream& out,pcap const& data){
            out<<data.header;
            for (pcaprec const& i:data.data) out<<i;
            return out;
        }
    };
    int main(void){
        std::ios::sync_with_stdio(false),std::cin.tie(nullptr),std::cout.tie(nullptr);
        pcap f;fin>>f;
        pcap nf;
        nf.header=f.header;
        for (auto i:f.data)
            if (i.header.orig_len<=1000) nf.data.push_back(i);
        sort(nf.data.begin(),nf.data.end(),
            [](pcaprec const& a,pcaprec const& b){
                return (a.header.ts_sec)*(uint64_t)1e6+a.header.ts_usec
                    <(b.header.ts_sec)*(uint64_t)1e6+b.header.ts_usec;
            }
        );
        fout<<nf;
        return 0;
    }
    

    由于 某些原因,我希望大家可以保持学术诚信,自己完成这道题目。作为回报,你的码力将会有所提升。

    • 1

    信息

    ID
    10042
    时间
    1000ms
    内存
    512MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者