리눅스

1차 과제 완료

정지홍 2023. 3. 29. 00:55
//2020y ~ 2024y
//-n은 이름, -y는 연도 , -a는 연봉
/*show -a*/	//전 직원 평균 연봉
/*show -n hong*/ //이름만 입력시 매년 정보 출력
/*show -a -y 2021*/ //해당연도 평균 연봉
/*show -n hong -y 2021*/ //이름 및 연도만 입력시 해당연도 연봉
//argc가 2인 경우:->./show -a 
//argc가 3인 경우:->./show -n Hong  
//argc가 4인 경우:->./show -a -y 2021 
//argc가 5인 경우:->./show -n Hong -y 2021
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void argc_a_y_year(int fd,int y);//argc가 2 혹은 4인 경우에 사용하는 함수입니다.
void argc_n_name(int fd,char *optarg,int y);//argc가 3 혹은 5인 경우에 사용하는 함수입니다.
int main(int argc , char *argv[]){

	int fd1,fd2,fd3,fd4,fd5;//파일 기술자 각각 txt1부터 txt5까지.
	int n;
	
	//차례대로 텍스트파일을 읽기 전용으로 열어줍니다.
	fd1=open("data1.txt",O_RDONLY);
	if (fd1==-1){
		perror("data1.txt");
		exit(1);
	}
	fd2=open("data2.txt",O_RDONLY);
	if (fd2==-1){
		perror("data2.txt");
		exit(1);
	}
	fd3=open("data3.txt",O_RDONLY);
	if (fd3==-1){
		perror("data3.txt");
		exit(1);
	}
	fd4=open("data4.txt",O_RDONLY);
	if (fd4==-1){
		perror("data4.txt");
		exit(1);
	}
	fd5=open("data5.txt",O_RDONLY);
	if (fd5==-1){
		perror("data1.txt");
		exit(1);
	}


	if(argc==2){//전달 받은 인자가 2개인 경우입니다.
			while( (n=getopt(argc,argv,"a"))!=-1){//argv에 -a존재시 반복하며 더이상 옵션이 없으면 -1을 리턴해서 반복문 탈출합니다.
				switch(n){
					case 'a'://매년 모든 직원의 연봉을 출력합니다.(./show -a)
						argc_a_y_year(fd1,2020);
						argc_a_y_year(fd2,2021);
						argc_a_y_year(fd3,2022);
						argc_a_y_year(fd4,2023);
						argc_a_y_year(fd5,2024);
					}
				}
	}

	if(argc==3){//전달받은 인자가 3개인 경우입니다.
			while( (n=getopt(argc,argv,"n:"))!=-1){//argv에 -n존재시 반복하며 :을 추가해서 이중 옵션으로 이름을 입력받습니다.
				switch(n){
					case 'n'://(./show -n Hong(옵션인자)인 경우)
					//optarg는 이중 옵션으로 받은 경우 뒤쪽에 있는 옵션이 저장되며 여기에서는 이름이 저장되어있습니다.
						argc_n_name(fd1,optarg,2020);
						argc_n_name(fd2,optarg,2021);
						argc_n_name(fd3,optarg,2022);
						argc_n_name(fd4,optarg,2023);
						argc_n_name(fd5,optarg,2024);
						break;
					}

				}
	}

	if(argc==4){//입력받은 인자가 4개인 경우(>show -a -y 2021)입니다.
		int isA=0;//-a가 입력되었는지 확인하는 변수입니다.
			while( (n=getopt(argc,argv,"y:a"))!=-1){//-y는 이중인자로 입력을 받으며 a는 인자를 받지 않습니다.
				switch(n){
					case 'a'://-a -y year을 위해서 우선 a가 입력되었는지 확인합니다. 이 케이스문 빠지면 isA=1로 변경합니다.
						isA=1;
						break;
					case 'y':
						if(isA==0){//처음 입력받은 인자가 a가 아닌경우 break를 걸어줍니다.
							break;
						}
						//optarg는 옵션의 인자값을 나타내며 getopt()호출마다 다시 설정됩니다. 인자가 없으면 널을 나타내줍니다.
						//이 경우에는 -a옵션의 인자는 널이며 -y의 인자는 연도를 전달받습니다.
						char *year=optarg;//전달받은 인자를 저장하는 변수입니다.
						if(strcmp(year,"2020")==0){//strcmp로 해당되는 연도를 찾습니다.
							argc_a_y_year(fd1,2020);
						}
						else if(strcmp(year,"2021")==0){
							argc_a_y_year(fd2,2021);
						}
						else if(strcmp(year,"2022")==0){
							argc_a_y_year(fd3,2022);
						}
						else if(strcmp(year,"2023")==0){
							argc_a_y_year(fd4,2023);
						}
						else if(strcmp(year,"2024")==0){
							argc_a_y_year(fd5,2024);
						}
						break;
					}

				}
	}

	if(argc==5){//전달 받은 인자가 5개인 경우입니다.(/show -n Hong -y 2021)
		char *name;//입력받은 이름을 저장할 변수입니다.
		char *year;//연도를 저장할 변수입니다.
		while( (n=getopt(argc,argv,"n:y:"))!=-1){//-n과 -y는 옵션인자를 같이 입력받습니다
			switch(n){
				case 'n':
					name=optarg;//입력받은 이름을 저장합니다.
				case 'y':
					year=optarg;//변수에 연도를 저장합니다.
					if (strcmp(year,"2020")==0){
						argc_n_name(fd1,name,2020);
					}
					else if (strcmp(year,"2021")==0){
						argc_n_name(fd2,name,2021);
					}
					else if (strcmp(year,"2022")==0){
						argc_n_name(fd3,name,2022);
					}
					else if (strcmp(year,"2023")==0){
						argc_n_name(fd4,name,2023);
					}
					else if (strcmp(year,"2024")==0){
						argc_n_name(fd5,name,2024);
					}
			}
		}
	}	
	//파일을 닫아줍니다.
	close(fd1);
	close(fd2);
	close(fd3);
	close(fd4);
	close(fd5);
	return 0;
}

void argc_a_y_year(int fd,int y){//argc가 2 혹은 4인 경우에 사용하는 함수입니다.
	char buf[1];//버퍼를 선언합니다.
	int n;//read함수에서 사용할 변수입니다. 리드함수는 실행마다 읽어온 크기만큼 오프셋이 이동합니다.
	int cnt=1;//텍스트 파일에 처음 숫자는 입사한 연도이니 이것을 구별하기 위한 변수입니다.(총 카운트값이 1~4이면 입사연도를 입력받고 있음을 뜻합니다.)
	int rst=0;//각각 자릿수마다 더 해질 변수입니다.
	int sum=0;//사원들의 연봉 합을 저장하는 변수입니다.
	int bunmo=0;//사원수를 카운트하는 변수입니다.
	float avg;//연봉 평균을 저장할 변수입니다.
						
	while((n=read(fd,buf,1))>0){//파일기술자가 가르키는 파일을 1바이트씩 읽습니다. 그리고 buf에 잠시 저장합니다. 한글은 2바이트이니 읽지 않습니다.
		if(*buf<48 || *buf>57){//버퍼에 숫자가 저장 안된 경우입니다.(아스키코드)
			if(rst>=1000){//연봉이 네자리 이상인 경우에 실행합니다.(한명의 사원의 연봉이 다 구해진 경우.)
				sum+=rst;//사원의 연봉을 총합에 더해줍니다.
				rst=0;//그리고 다음 사원을 검색하기 위해 rst=0을 해줍니다.
				bunmo+=1;//사원수를 1명 더해줍니다.
				cnt=1;//다음 검색을 위해 카운트=1을 해줍니다.
			}
		}
		if(*buf>=48 && *buf<=57){//버퍼에 숫자가 저장된 경우입니다.(아스키코드)
			int save=*buf;//버퍼에 저장된 수를 save변수에 저장합니다.
			save-=48;//아스키코드에서 십진수로 바꿔야하니 48을 빼줍니다.
				if(cnt>=5 && cnt<=9){//앞의 cnt가 1~4인경우는 입사년도이며 5~8or9는 연봉을 나타내는 숫자입니다.
					if(rst==0){//cnt=5인 경우이며 최종적으로는 천의 자리 숫자 혹은 만의 자리 숫자입니다.
						rst=save;//rst에 저장해줍니다.
						cnt+=1;//다음 자리수를 위해 cnt+=1일 합니다.
					}
					else{//cnt=6,7,8,9인 경우입니다. 연봉이 만의 자리 숫까지 가는 경우만 cnt=9까지 도달합니다.
						rst*=10;//기존 숫자에 자릿수를 앞으로 늘립니다.
						rst+=save;//뒤에 숫자를 더해줍니다.
						cnt+=1;//다음 연산을 위해서 1을 더해줍니다.
					}
				}
				else{//입사년도에 해당되는 케이스입니다.
					cnt+=1;//입사년도와연봉 구별하는 변수에 1을 더해줍니다.
				}
			}
	}
	avg=sum/bunmo;//평균 연봉을 구합니다.
	printf("%d년 직원의 평균 연봉은 %.2f입니다.\n",y,avg);

}

void argc_n_name(int fd,char *optarg,int y){//argc가 3 혹은 5인 경우에 사용하는 함수입니다.
//함수는 파일기술자와 이름 그리고 연도를 차례로 입력받습니다.
	int n;//read함수에서 사용할 변수입니다. 리드함수는 실행마다 읽어온 크기만큼 오프셋이 이동합니다.
	char buf[512];//버퍼를 선언합니다.
	memset(buf,'\0',sizeof(buf));//memset함수를 이용하여 buf가 가르키는 메모리를 초기화합니다.
	while((n=read(fd,buf,1))>0){//파일 기술자가 가르키는 연도의 사원 파일에서 1바이트씩 읽습니다. 그리고 buf에 저장합니다.
		char *name=optarg;//optarg는 옵션의 인자값을 나타내며 getopt()호출마다 다시 설정됩니다. 인자가 없으면 널을 나타내줍니다.
		char rtn[10]="";//파일을 읽으면서 이름을 잠시 저장할 변수입니다.
		if(*buf>=65 && *buf<=90){//-n과 이름을 입력받으며 성의 첫글자는 대문자이니 아스키코드로 구분합니다.
	//단, 근무 평점도 대문자입니다. 그래서 아래 반복문으로 구별할 예정입니다.
	//만약 지금이 근무 평점이면 다음에 읽는 문자도 대문자 알파벳입니다.
			strncat(rtn,buf,strlen(buf));//빈 문자열에 buf에 저장된 대문자를 더합니다.
			n=read(fd,buf,1);//그리고 다음 문자를 읽어줍니다.		
			//아래 반복문은 만약에 지금 근무평점이면 while문에 빠지지 않으며 아래의 if문에 들어가도 optarg로 전달받은 이름과 비교해도 조건에 만족을 못합니다.
			while(*buf>=97 && *buf<=122){//이번에 읽은 문자가 소문자인 경우에만 반복문이 실행됩니다.
				strncat(rtn,buf,strlen(buf));//저장된 문자열에 계속해서 문자를 더해줍니다.
				n=read(fd,buf,1);//다음 문자를 읽어줍니다.						
			}
			if ((strcmp(rtn,name)==0)){//현재 rtn에 저장된 이름과 name이 일치한 경우 사원 정보를 출력을 시작합니다. 
				printf("%d년->이름:%s , 입사연도:",y,name);
				n=read(fd,buf,5);//리드함수는 실행마다 읽어온 크기만큼 오프셋이 이동하니 이걸 이용하여 사원 정보를 읽고 출력합니다.
				printf("%s",buf);
				printf(", 근무부서: ");
				n=read(fd,buf,10);//다시 read함수로 읽어오고 다음 출력을 위해 오프셋을 이동시켜줍니다.
				printf("%s. 연봉: ",buf);
				buf[5]='\0';//다음을 위해서 버퍼 6번째 자리에 널을 넣어줍니다.
				n=read(fd,buf,5);//다시 read함수로 읽어오고 다음 출력을 위해 오프셋을 이동시켜줍니다.
				printf("%s. ",buf);
				buf[2]='\0';//다음을 위해서 버퍼 3번째 자리에 널을 넣습니다.
				n=read(fd,buf,2);//다시 read함수로 읽어오고 다음 출력을 위해 오프셋을 이동시켜줍니다.
				printf("근무 평점: %s\n",buf);
				break;
			}
								
		}
	}
}