理解C对位和偏移的要求

时间:2017-03-26 20:36:29

标签: c logging offset unsigned-char

我理解C的一般概念以及如何制作日志文件。读/写文件等。

我关注的是以下格式:

[![在此处输入图像说明] [1]] [1]

我现在已经完成了一个很好的块,但是我担心在第一条记录之后如何附加到我的日志文件中。我递增文件的记录计数(在前2个字节中)并在其后写入第一个记录。然后,我将如何设置将第2 /第3 / etc记录添加到彼此之后?

//confirm a file exists in the directory
bool fileExists(const char* file)
{
    struct stat buf;
    return (stat(file, &buf) == 0);
}

int rightBitShift(int val, int space)
{
    return ((val >> space) & 0xFF);
}

int leftBitShift(int val, int space)
{
    return (val << space);
}

int determineRecordCount(char * logName)
{
    unsigned char record[2];
    FILE *fp = fopen(logName, "rb");
    fread(record, sizeof(record), 1, fp); 

    //display the record number
    int recordNum = (record[0] << 8) | record[1];
    recordNum = recordNum +1;

    return (recordNum);
}


void createRecord(int argc, char **argv)
{
    int recordNum;
    int aux = 0;
    int dst;
    char* logName;
    char message[30];
    memset(message,' ',30);

    //check argument count and validation 
    if (argc == 7 && strcmp("-a", argv[2]) ==0 && strcmp("-f", argv[3]) ==0 && strcmp("-t", argv[5]) ==0)
    {
        //aux flag on
        aux = 1;
        logName = argv[4];
        strncpy(message, argv[6],strlen(argv[6]));
    }
    else if (argc == 6 && strcmp("-f", argv[2]) ==0 && strcmp("-t", argv[4]) ==0)
    {
        logName = argv[3];
        strncpy(message, argv[5],strlen(argv[5]));
    }
    else
    {
        printf("Invalid Arguments\n");
        exit(0);
    }

    //check if log exists to get latest recordNum
    if (fileExists(logName))
    {
        recordNum = determineRecordCount(logName);
        printf("%i\n",recordNum);
    }
    else
    {
        printf("Logfile %s not found\n", logName);
        recordNum = 1;
    }


    //Begin creating record
    unsigned char record[40]; /* One record takes up 40 bytes of space */
    memset(record, 0, sizeof(record));

    //recordCount---------------------------------------------------------------------
    record[0] = rightBitShift (recordNum, 8); /* Upper byte of sequence number */
    record[1] = rightBitShift (recordNum, 0); /* Lower byte of sequence number */

    //get aux/dst flags---------------------------------------------------------------
    //get date and time
    time_t timeStamp = time(NULL);
    struct tm *date = localtime( &timeStamp );
    if (date->tm_isdst)
        dst = 1;
    record[2] |= aux << 7;  //set 7th bit
    record[2] |= dst << 6;  //set 6th

    //timeStamp-----------------------------------------------------------------------
    record[3] |= rightBitShift(timeStamp, 24);//high byte
    record[4] |= rightBitShift(timeStamp, 16);
    record[5] |= rightBitShift(timeStamp, 8);
    record[6] |= rightBitShift(timeStamp, 0); //low byte

    //leave bytes 7-8, set to 0 -----------------------------------------
    record[7] = 0;
    record[8] = 0;

    //store message--------------------------------------------
    strncpy(&record[9], message, strlen(message));


    //write record to log-----------------------------------------------------------------
    FILE *fp = fopen(logName, "w+");

     unsigned char recordCount[4];
    recordCount[0] = rightBitShift (recordNum, 8); /* Upper byte of sequence number */
    recordCount[1] = rightBitShift (recordNum, 0); /* Lower byte of sequence number */
    recordCount[2] = 0;
    recordCount[3] = 0;

    fwrite(recordCount, sizeof(recordCount), 1, fp);

    fwrite(record, sizeof(record), 1, fp);
    fclose(fp); 

    printf("Record saved successfully\n");
}

2 个答案:

答案 0 :(得分:1)

注意:在C之前,我从来没有这样做,带着一粒盐。

这是一种非常具体的二进制格式,其中每个位都被精确考虑。它使用Least-Significant-Bit numbering scheme (LSB 0),其中位从7到0编号。

指定&#34;高位字节&#34;首先表示此格式为big-endian。最重要的位首先出现。这就像我们写下我们的数字一样,四千三百二十一是4321. 1234将是小尾数。例如,记录数和序列都是16位大端数。

最后,checksum是从记录的其余部分计算的数字,以验证传输中没有错误。规范定义了如何进行校验和。

您的工作是精确复制此格式,可能使用fixed-sized types found in stdint.hunsigned char。例如,序列可以是uint16_tunsigned char[2]

生成记录的函数可能有这样的签名:

unsigned char *make_record( const char *message, bool aux );

用户只需向您提供消息和辅助标志。其余的你可以通过功能弄清楚。您可能决定让它们传递时间戳和顺序。重点是,函数只需要传递数据,它负责格式化。

这种字节顺序意味着你不能写出整数,它们可能是错误的大小或错误的字节顺序。这意味着必须先序列化任何多字节整数,然后才能将它们写入记录。 This answer covers ways to do that并且我将使用this answer中的那些,因为它们更方便了。

#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

unsigned char *make_record( const char *message, bool aux ) {
    // Allocate and zero memory for the buffer.
    // Zeroing means no risk of accidentally sending garbage.
    unsigned char *buffer = calloc( 40, sizeof(unsigned char) );

    // As we add to the buffer, pos will track the next byte to be written.
    unsigned char *pos = buffer;

    // I decided not make the user responsible for
    // the sequence number. YMMV.
    static uint16_t sequence = 1;    
    pos = serialize_uint16( pos, sequence );

    // Get the timestamp and DST.
    time_t timestamp = time(NULL);
    struct tm *date = localtime( &timestamp );

    // 2nd row is all flags and a bunch of 0s. Start with them all off.
    uint8_t flags = 0;
    if( aux ) {
        // Flip the 7th bit on.
        flags |= 0x80;
    }
    if( date->tm_isdst ) {
        // Flip the 6th bit on.
        flags |= 0x40;
    }

    // That an 8 bit integer has no endianness, this is to ensure
    // pos is consistently incremented.
    pos = serialize_uint8(pos, flags);

    // I don't know what their timestamp format is.
    // This is just a guess. It's probably wrong.
    pos = serialize_uint32(pos, (uint32_t)timestamp);

    // "Spare" is all zeros.
    // The spec says this is 3 bytes, but only gives it bytes
    // 7 and 8. I'm going with 2 bytes.
    pos = serialize_uint16(pos, 0);

    // Copy the message in, 30 bytes.
    // strncpy() does not guarantee the message will be null
    // terminated. This is probably fine as the field is fixed width.
    // More info about the format would be necessary to know for sure.
    strncpy( pos, message, 30 );
    pos += 30;

    // Checksum the first 39 bytes.
    // Sorry, I don't know how to do 1's compliment sums.
    pos = serialize_uint8( pos, record_checksum( buffer, 39 ) );

    // pos has moved around, but buffer remains at the start
    return buffer;
}

int main() {
    unsigned char *record = make_record("Basset hounds got long ears", true);
    fwrite(record, sizeof(unsigned char), 40, stdout);
}

此时我的专业知识已经筋疲力尽,我以前从未这样做过。我很感激大家修复编辑中的小错误,并建议在评论中更好的方法,比如如何处理时间戳。也许其他人可以在另一个答案中介绍如何进行1恭维校验和。

答案 1 :(得分:0)

由于字节由8位(从0到7)组成,因此您可以按照规范中的要求使用按位运算来修改它们。查看一般信息(https://en.wikipedia.org/wiki/Bitwise_operations_in_C)。作为预览,您可以使用&gt;&gt;或&lt;&lt;运算符确定要修改哪个位,并使用逻辑运算符|和&amp;设定它的价值观。