注意
これはある課題の1つなので、課題の題意の意図に沿って実装します
概要1
どういったことをするかをまず書く
1)ファイルディスクリプタを受け取る
2)そのファイルからBUFFER_SIZE分読み込む
3)改行までをリターンする
ただし、ここでは以下の関数のみが使える
read, malloc, free
コンパイルオプションは以下
$ gcc -Wall -Wextra -Werror -D BUFFER_SIZE="任意の値" get_next_line.c get_next_line_utils.c
つまり、こんな感じのmain.c
を作ってテストファイルの内容を出力できればOKってこと
main.c
#include <stdio.h>
#include <fcntl.h>
#include "get_next_line.h"
int main(void)
{
int fd;
char *line;
line = "";
fd = open("test.txt", O_RDONLY);
while (line)
{
line = get_next_line(fd);
printf("> %s", line);
free(line);
}
return (0);
}
基本的な関数
get_next_line_utils.c
に基本的な関数を書いていく
stdlib.h
に入っているような関数は自作です
内容については触れません
get_next_line_utils.c
#include "get_next_line.h"
size_t ft_strlen(const char *s)
{
size_t return_len;
return_len = 0;
while (s[return_len] != '\0')
return_len ++;
return (return_len);
}
char *ft_strchr(char *s, int c)
{
int i;
i = 0;
if (!s)
return (0);
if (c == '\0')
return ((char *)&s[ft_strlen(s)]);
while (s[i] != '\0')
{
if (s[i] == (char) c)
return ((char *)&s[i]);
i++;
}
return (0);
}
char *ft_strjoin(char *s1, char *s2)
{
size_t i;
size_t c;
char *rtn;
if (!s1)
{
s1 = (char *)malloc(1 * sizeof(char));
s1[0] = '\0';
}
if (!s1 || !s2)
return (NULL);
rtn = malloc((ft_strlen(s1) + ft_strlen(s2) + 1) * sizeof(char));
if (rtn == NULL)
return (NULL);
i = -1;
c = 0;
if (s1)
while (s1[++i] != '\0')
rtn[i] = s1[i];
while (s2[c] != '\0')
rtn[i++] = s2[c++];
rtn[ft_strlen(s1) + ft_strlen(s2)] = '\0';
free(s1);
return (rtn);
}
心臓部分の実装
こんな感じで形を作ります。
何回も呼び出されるのでstatic char
で保存する部分を定義します
get_next_line.c
/*
lineを定義(CHAR)
saveを定義(STATIC CHAR)
もし、fdが0より小さいもしくはBUFFER_SIZEが0以下の場合
0を返す
saveに読み込んだファイルの内容を格納
もし、saveがなかった場合
NULLを返す
lineに改行が来るまでを入れる
saveに改行後を入れる
lineをリターンする
*/
#include "get_next_line.h"
char *get_next_line(int fd)
{
char *line;
static char *save;
if (fd < 0 || BUFFER_SIZE <= 0)
return (0);
save = ft_read(fd, save);
if (!save)
return (NULL);
line = ft_get_line(save);
save = ft_save(save);
return (line);
}
サブ関数を作っていきます。
まず、saveに'\n'が来るまでファイルを読み込み、代入する関数を作ります
get_next_line.c
/*
tmpを定義(CHAR)
read関数の返り値を格納するread_rtnを定義(INT)
tmpの領域をBUFFER_SIZE+1分、charのサイズで確保する
もし、tmpがNULLの場合
NULLを返す(mallocが失敗したときのため)
read_rtn=1で初期化
saveに'\n'が入っていない、かつread_rtnが0でない場合
BUFFER_SIZE分、ファイルを読み、tmpに格納する
もし、read_rtnが-1の場合(readに失敗した場合)
tmpをフリーする
NULLを返す
tmpをNULL止めする
saveにtmpをくっつけて再代入
tmpをフリーする
saveを返す
*/
char *ft_read(int fd, char *save)
{
char *tmp;
int read_rtn;
tmp = malloc((BUFFER_SIZE + 1) * sizeof(char));
if (!tmp)
return (NULL);
read_rtn = 1;
while (!ft_strchr(save, '\n') && read_rtn != 0)
{
read_rtn = read(fd, tmp, BUFFER_SIZE);
if (read_rtn == -1)
{
free(tmp);
return (NULL);
}
tmp[read_rtn] = '\0';
save = ft_strjoin(save, tmp);
}
free(tmp);
return (save);
}
saveの中から改行が来るまでを取り出して、返す関数を作ります
get_next_line.c
/*
汎用カウンターiを定義(INT)
'\n'の前までを保存する*rtnを定義(CHAR)
i=0で初期化
もし、saveが空な場合
NULLを返す
save[i]に内容がある間、かつsave[i]が'\n'でない場合
iに1ずつ足していく('\n'を含まずに文字数をカウントしている)
マロックでrtnの領域を確保する('\n'と'\0'の場所のため+2している)
もし、rtnがない場合
NULLを返す(mallocが失敗したときのため)
i=0で初期化
save[i]に内容がある間、かつsave[i]が'\n'でない場合
rtn[i]にsave[i]を代入する
iに1を足す
もし、save[i]に'\n'が来ていた場合
rtn[i]に'\n'を代入する
rtn[i]をNULL止めする
rtnを返す
*/
char *ft_get_line(char *save)
{
int i;
char *rtn;
i = 0;
if (!save[i])
return (NULL);
while (save[i] && save[i] != '\n')
i++;
rtn = (char *)malloc(sizeof(char) * (i + 2));
if (!rtn)
return (NULL);
i = 0;
while (save[i] && save[i] != '\n')
{
rtn[i] = save[i];
i++;
}
if (save[i] == '\n')
{
rtn[i] = '\n';
i++;
}
rtn[i] = '\0';
return (rtn);
}
saveの中から改行の後を取り出して、返す関数を作ります
get_next_line.c
/*
汎用カウンターiを定義(INT)
汎用カウンターcを定義(INT)
'\n'の後ろを保存する*sを定義(CHAR)
i=0で初期化
save[i]があり、かつsave[i]が'\n'でない場合、
iに1を足し続ける
もし、save[i]がない場合、('\n'の後ろに何もない場合)
saveをフリーする
NULLを返す
sを「saveの長さ - '\n'までの長さ + 1」で確保する('\n'の後ろとNULL分確保)
もし、sがNULLの場合
NULLを返す(mallocが失敗したときのため)
iに1を足す
c = 0で初期化
save[i]がNULL出ない場合
s[c++] = save[i]で'\n'以降を代入し続ける
sをNULL止めする
saveをフリーする
sを返す
*/
char *ft_save(char *save)
{
int i;
int c;
char *s;
i = 0;
while (save[i] && save[i] != '\n')
i++;
if (!save[i])
{
free(save);
return (NULL);
}
s = (char *)malloc((ft_strlen(save) - i + 1) * sizeof(char));
if (!s)
return (NULL);
i++;
c = 0;
while (save[i])
s[c++] = save[i++];
s[c] = '\0';
free(save);
return (s);
}
検証
$ cat test.txt -e
hello world$
hello$
$
$
$
$
$
$
$
$
$
$
a$
g$
a$
gr@[rgeapage,pg@agre$
ae$
gpamtamt$
$
$
$
$
$
aopt,,,,,gbg$
$ clear && gcc -Wall -Wextra -Werror -D BUFFER_SIZE=42 get_next_line.c get_next_line_utils.c main.c -g && ./a.out | cat -e
> hello world$
> hello$
> $
> $
> $
> $
> $
> $
> $
> $
> $
> $
> a$
> g$
> a$
> gr@[rgeapage,pg@agre$
> ae$
> (null)$
でけた!
詳しい説明とかできてないけど、もしかしたら書き加えるかも
参考サイト