/****************************************************************
*File Name : final?.c
*Data : 04/11/28
*Author : Solid Snake(wzz24)
*Model :
*
*Description : Get data from primitive tel-paper, and
then count everybody's fee
*................................................................
*Revision History
*No Data Revised FunctionName
*1 04/11/10 MultiArray[] 第一阶段用的是多维数组写的, 刚开始接触C,没有经验
*2 04/11/28 LinkTable 第二阶段用的是连表写的, 别人都这么写, 我也就改成了连表
*3 04/12/12 模块化 第三阶段把具体功能拆分我认为还可以了, 便于添加新功能
*4 //继续优化中 原来冗余的变量减少
*5 //优化 驱除空格的错误 犯了低级错误, 不知什么原因, copy的Tab后变成了连续的空格, 我倒!!!害我几
乎一个礼拜时间, shit
*6 //优化 减少冗余变量 2个GlobalVariable, main()里3个AutoVariable,自我感觉良好,比较满意,
*7 05/01/01 优化
*8 05/01/02 想改成用makefile的(但没成功, changing...)
****************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define NEW_UNIT 13
/*************用此结构体存储一个单元,一个单元就是用空行(共2个字符ASCII码是13和10)隔开的******/
struct Bill_t
{
char cUserName[BUFSIZ];
char cType[BUFSIZ];
char cTime[BUFSIZ];
char cIno[BUFSIZ];
char cOuto[BUFSIZ];
struct Bill_t *Next;
};
typedef struct Bill_t stNode;
typedef stNode *Link;
/*************用此结构体存储一个单元,一个单元就是用空行(共2个字符ASCII码是13和10)隔开的******/
/****************用此结构存储不重复的用户名*******************/
struct Name_t
{
char cUserName[BUFSIZ];
struct Name_t *Next;
};
typedef struct Name_t stNodeName;
typedef stNodeName *LinkName;
/****************用此结构存储不重复的用户名*******************/
/****************函数原型声明*************************/
LinkName GetUserName(Link);
Link CreateList(Link, FILE *);
void CountFee(LinkName, Link);
void getValue(char *, char * , char *);
Link FilterSame(Link);
/****************函数原型声明*************************/
/****************全局变量**********************/
char cBuf[BUFSIZ]; //每一行的内容都放在这里
int iUnit; //判断是否为头节点
/****************全局变量**********************/
int main(void)
{
/***********************变量定义开始*************************/
Link Head; //存储每个单元的头节点
LinkName HeadName; //指向用户名连表的头节点
FILE *Stream;
/***********************变量初始化开始**********************/
Stream = fopen("detail.txt","r");
iUnit = 1; //初始化全局变量 iUnit
/***********************生成连表*****************************/
Head = CreateList(Head, Stream);
/***********************用GetUserName()取得无重复用户名字列表**/
HeadName = GetUserName(Head);
/***********************话单分捡(还没测试)****************************/
/********************Head = FilterSame(Head);***********************/
/***********************计算用户费用****************************/
CountFee(HeadName, Head);
}
/*******************************函数定义区************************************/
/************************创建连表***************************************/
Link CreateList(Link Head, FILE *Stream)
{
Link New;
Link Pointer; //连接连表的各个节点用
Head = (Link)malloc(sizeof(stNode)); //Initialize Global Variable Head
New = (Link)malloc(sizeof(stNode)); //Initialize Global Variable New
/***此处while循环目的是分别把每一个单元放到连表的节点BEGIN***/
while (fgets(cBuf, BUFSIZ, Stream) != NULL) //针对每一行进行判断
{ //sizeof(cBuf)返回4, 因为cBuf是指针(sizeof(*)等于4), 即使把形参改成char cBuf[], 还是4, 因为它还是一个指针, 所以不能用sizeof获取数组大小, 可以使用宏BUFSIZ
//如果STATUS=STOP, 就加入连表(还没写)
if (cBuf[0] == NEW_UNIT) //如果每一行的第一个字符(ASCII码)是13,意味一个新的单元的开始
{
iUnit++; //发现一个单元才加1
if (2 == iUnit) //开始第二个单元时,把Head over,Pointer指向Head
{ //不是当iUnit==2时每次都执行,因为cBuf[0]==13每个单元(节点)只有一次机会
Head->Next = NULL;
Pointer = Head;
}
if (iUnit > 2) //当iUnit>2时
{
New->Next = NULL; //先设置旧节点为NULL
Pointer->Next = New; //Pointer和新节点相连
Pointer = New; //Pointer指向New
New = (Link)malloc(sizeof(stNode)); //新单元开始,创建新节点
}
}
else //如果不是,说明在一个单元内
{
/*********获取用户名BEGIN**********/
getValue(" User-Name", Head->cUserName, New->cUserName);
/*********获取用户名OVER***********/
/*********获取类型BEGIN************/
getValue(" Acct-Status-Type", Head->cType, New->cType);
/*********获取类型OVER*************/
/*********获取时间BEGIN************/
getValue(" Acct-Session-Time", Head->cTime, New->cTime);
/*********获取时间OVER*************/
/*********获取流入量BEGIN**********/
getValue(" Acct-Input-Octets", Head->cIno, New->cIno);
/*********获取流入量OVER***********/
/*********获取流出量BEGIN**********/
getValue(" Acct-Output-Octets", Head->cOuto, New->cOuto);
/*********获取流出量OVER***********/
}
}
/**********此处while循环目的是分别把每一个单元放到连表的节点中OVER***********/
return Head;
}
/******************取得我要找的值, 并且赋值给相应的节点***********************************/
void getValue(char *cName, char *HeadValue, char *NewValue)
{
//cName是要找的字符串 eg:User-Name, 即名/值对中的名
//cValue是我要找的字符串所对应的值, 即名/值对中的值
/*
cBufName是临时的, 用于判断某一行是否是我要找的
eg: 我要找某一行开头有"UserName"的行, 那我就用cBufName存储与"UserName"相同长度的每一行的值,
再把cBufName与"UserName"比较, 就可知道是否当前行是我的目标行
*/
//HeadValue是Bill_t内元素的地址, 这样只需在main函数内把相应的Bill_t元素地址传给本函数, 就可以赋值了
//NewValue同上, 但它是后面的节点, 而HeadValue是对应的头节点的
int iName; //for circle count, trace username
int iValue; //trace username-value
char cBufName[BUFSIZ];
char cValue[BUFSIZ];
for (iName = 0; iName < strlen(cName); iName++)
cBufName[iName] = cBuf[iName]; //cBuf is global variable
cBufName[iName] = '/0';
if (strcmp(cBufName, cName) == 0) //找到目标行
{
iName = strlen(cBufName) + 3;
iValue = 0;
while (cBuf[iName] != '/0')
cValue[iValue++] = cBuf[iName++];
cValue[iValue - 2] = '/0'; //remove '/n'
if (1 == iUnit) //iUnit is Global variable
strcpy(HeadValue, cValue);
else
strcpy(NewValue, cValue);
}
}
/**********************建立不重复用户名连表***********************/
LinkName GetUserName(Link Head)
{
/*****************对auto变量初始化**************/
int iSign;
int iUnitName = 0;
Link Pointer = Head;
LinkName HeadName;
LinkName NewName;
LinkName PointerName;
LinkName PointerTest;
/*************对auto变量初始化****************/
HeadName = (LinkName)malloc(sizeof(stNodeName));
NewName = (LinkName)malloc(sizeof(stNodeName));
while (Pointer != NULL)
{
//跟每个节点进行比较时都要把iSign清0,否则当iSign被置1后,后面就无法继续
//因为iSign一直是1,即使后面不是重名的节点也没有往连表上连
iSign = 0;
iUnitName++;
if (1 == iUnitName) //如果是头节点,给头节点赋值
{
strcpy(HeadName->cUserName, Pointer->cUserName);
HeadName->Next = NULL;
PointerName=HeadName;
}
else //不是头节点
{
//再对新生成的连表遍历,看要往新生成的连表上连的Pointer节点的名字是否与新生成的连表重名
//Pointer->cUserName是我要往新生成的连表上连的节点的名字
PointerTest = HeadName;
while (PointerTest != NULL)
{
if (strcmp(Pointer->cUserName, PointerTest->cUserName) == 0) //重复
{
iSign = 1; //证明有重复的
break;
}
PointerTest=PointerTest->Next;
}
if (iSign != 1)
{
strcpy(NewName->cUserName, Pointer->cUserName); //没有重复的就赋值
NewName->Next = NULL;
PointerName->Next = NewName;
PointerName = PointerName->Next;
NewName = (LinkName)malloc(sizeof(stNodeName));
}
Pointer = Pointer->Next;
}
}
return HeadName;
}
/***************************计费***********************/
void CountFee(LinkName HeadName, Link Head)
{
int TotalTime;
int TotalIno;
int TotalOuto;
Link Pointer;
LinkName PointerName = HeadName;
while (PointerName != NULL)
{
TotalTime = 0;
TotalIno = 0;
TotalOuto = 0;
Pointer = Head;
while (Pointer != NULL)
{
if (strcmp(PointerName->cUserName, Pointer->cUserName) == 0)
{
TotalTime += atoi(Pointer->cTime);
TotalIno += atoi(Pointer->cIno);
TotalOuto += atoi(Pointer->cOuto);
}
Pointer = Pointer->Next;
}
printf("%s Time is %d, Ino is %d, Outo is %d/n", PointerName->cUserName,TotalTime, TotalIno, TotalOuto);
PointerName = PointerName->Next;
}
}
/**********************话单分捡(去除重单)*******************************/
/*Link FilterSame(Link Head) (还没测试)
{
Link Pointer;
Link PointerSon;
Link Back; //跟踪Pointer, 当要删除中间节点时有作用
Pointer = Head;
int iFlag; //标志是否有重单
//嵌套循环判断是否重单
while (Pointer != NULL)
{
iFlag = 0; //每一次新的开始, 重置iFlag
PointerSon = Pointer->Next; //为了与Pointer后面的节点比较, 所以新建PointerSon指向Pointer的
//下一个节点
while (PointerSon != NULL)
{
if (strcmp(Pointer->Uid, PointerSon->Uid) == 0 && strcmp(Pointer->ConTime, PointerSon->ConTime) == 0)
{
iFlag = 1; //如果符合重单规则, 那么置iFlag为1, 代表有重单
break; //立即跳出子循环
}
PointerSon = PointerSon->Next;
}
if (iFlag) //有重单, 那么删除Pointer这个节点,(我是把前面的节点删除)
{
if (Pointer == Head) //如果是删除头节点
{
Head = Pointer->Next;
free(Pointer); //释放空间
Pointer = Head; //Pointer指向新的头节点(即指向下一个节点)
}
else //否则是删除中间节点(注意, 不可能是尾节点, 因为我可前面的节点删除)
{ //------可能存在问题, 注意这里
Back = Pointer->Next;
free(Pointer);
Pointer = Back; //Pointer指向下一个节点
}
}
else //没有重单, 那么继续往下判断
Back = Pointer; //目的是设置Back在Pointer的后面
Pointer = Pointer->Next; //目的是设置Back在Pointer的后面
}
return Head;
}
*/