크기와 개수를 인수로 취하는 fread / fwrite의 근거는 무엇입니까?


96

우리는 왜 fread와 fwrite가 멤버 당 크기를 취하고 버퍼와 크기를 취하는 대신 읽고 / 쓰기 된 멤버의 수를 계산하고 반환하는 이유에 대해 여기서 논의했습니다. 우리가 생각해 낼 수있는 유일한 용도는 플랫폼 정렬로 균등하게 나눌 수 없기 때문에 패딩 된 구조체 배열을 읽고 / 쓰려는 경우입니다. 디자인.

에서 FREAD (3) :

fread () 함수는 stream이 가리키는 스트림에서 각 크기 바이트 길이의 데이터의 nmemb 요소를 읽고 ptr이 지정한 위치에 저장합니다.

fwrite () 함수는 각 크기 바이트 길이의 데이터의 nmemb 요소를 stream이 가리키는 스트림에 기록하여 ptr이 지정한 위치에서 가져옵니다.

fread () 및 fwrite ()는 성공적으로 읽거나 쓴 항목 수 (즉, 문자 수가 아님)를 반환합니다. 오류가 발생하거나 파일 끝에 도달하면 반환 값은 짧은 항목 수 (또는 0)입니다.


10
좋은 질문입니다. 나는 항상 그것에 대해 궁금했다
Johannes Schaub-litb

답변:


22

그것은 fread 가 구현 되는 방법에 기반합니다 .

단일 UNIX 사양에 따르면

각 개체에 대해 크기 호출이 fgetc () 함수에 대해 수행되고 결과는 읽은 순서대로 개체를 정확히 오버레이하는 부호없는 문자 배열에 저장됩니다.

fgetc 에는 다음과 같은 메모도 있습니다.

fgetc ()는 바이트에서 작동하므로 여러 바이트 (또는 "다중 바이트 문자")로 구성된 문자를 읽으려면 fgetc ()를 여러 번 호출해야 할 수 있습니다.

물론 이것은 UTF-8과 같은 멋진 가변 바이트 문자 인코딩보다 이전입니다.

SUS는 이것이 실제로 ISO C 문서에서 가져온 것이라고 언급합니다.


72

fread (buf, 1000, 1, stream) 및 fread (buf, 1, 1000, stream)의 차이점은 첫 번째 경우 파일이 더 작고 두 번째 경우 파일의 모든 것을 1000 바이트 이하로 가져옵니다.


4
사실이지만 그것은 이야기의 작은 부분에 불과합니다. int 값의 배열 또는 구조의 배열과 같이 읽는 것을 대조하는 것이 좋습니다.
Jonathan Leffler

3
정당화가 완료되면 훌륭한 대답이 될 것입니다.
Matt Joiner

13

이것은 순수한 추측이지만, 과거에는 (일부는 여전히 주변에 있음) 많은 파일 시스템이 하드 드라이브의 단순한 바이트 스트림이 아니 었습니다.

많은 파일 시스템이 레코드 기반이므로 효율적인 방식으로 이러한 파일 시스템을 만족 시키려면 항목 수 ( "레코드")를 지정해야합니다. 그러면 fwrite / fread가 저장소에서 바이트 스트림이 아닌 레코드로 작동 할 수 있습니다.


1
누군가이 문제를 제기해서 기쁩니다. 파일 시스템 사양과 FTP로 많은 작업을 수행했으며 레코드 / 페이지 및 기타 차단 개념은 더 이상 사양의 해당 부분을 사용하지 않지만 매우 확고하게 지원됩니다.
Matt Joiner

9

여기에서 해당 기능을 수정하겠습니다.

size_t fread_buf( void* ptr, size_t size, FILE* stream)
{
    return fread( ptr, 1, size, stream);
}


size_t fwrite_buf( void const* ptr, size_t size, FILE* stream)
{
    return fwrite( ptr, 1, size, stream);
}

매개 변수에 대한 이론적 근거는 fread()/fwrite() 는 K & R 사본을 오래 전에 잃어 버렸기 때문에 추측 만 할 수 있습니다. 아마도 Kernighan과 Ritchie가 바이너리 I / O를 수행하는 것이 객체 배열에서 가장 자연스럽게 수행 될 것이라고 생각했을 수 있다고 생각합니다. 또한 블록 I / O가 구현하는 것이 더 빠르거나 더 쉬울 것이라고 생각했을 수도 있습니다.

비록 C 표준 지정 fread()fwrite()측면에서 구현 될fgetc() 하고 fputc(), 표준은 C가 K & R에 의해 표준 힘에 지정된 일을하지 원래 디자이너의 아이디어에 있었던 것으로 정의 된 후에도 오랫동안 존재로 온 것을 기억한다. K & R의 "The C Programming Language"에서 말한 내용이 언어가 처음 설계되었을 때와 같지 않을 수도 있습니다.

마지막으로 PJ Plauger가 fread()"The Standard C Library"에서 말한 내용 은 다음과 같습니다.

는 IF size(두 번째) 인수가 1보다 큰, 당신은 함수가도를 읽을 여부를 확인할 수 없습니다 size - 1그것을보고하는 것 이상 추가 문자. 원칙적으로, 당신은 함수를 호출 더 낫다 fread(buf, 1, size * n, stream);대신 fread(buf, size, n, stream);

기본적으로 그는 fread()인터페이스가 손상 되었다고 말합니다 . 들어 fwrite()내가 동의하지 않을 성명 - 그가 그 노트, "이 큰 단점이되지 않도록 쓰기 오류는 일반적으로 드물다".


17
사실 저는 종종 다른 방식으로하는 것을 좋아합니다. fread(buf, size*n, 1, stream);불완전한 읽기가 오류 조건이라면 읽은 fread바이트 수보다 단순히 0 또는 1을 반환 하도록 배열하는 것이 더 간단합니다 . 그런 다음 if (!fread(...))요청 된 바이트 수 (추가 C 코드 및 추가 기계 코드가 필요함)와 결과를 비교하는 대신 같은 작업을 수행 할 수 있습니다 .
R .. GitHub STOP HELPING ICE

1
@R ..! fread (...) 외에도 크기 * count! = 0을 확인하십시오. size * count == 0이면 성공한 경우 0 반환 값을 얻 습니다. 읽기 (0 바이트)에서 feof () 및 ferror ()가 설정되지 않으며 errno는 ENOENT와 같이 무의미하거나 더 나빠집니다. , EAGAIN과 같은 오해의 소지가있는 (그리고 아마도 비판적으로 깨지는) 무언가-매우 혼란 스럽습니다. 특히 기본적으로이 문제를 비명하는 문서가 없기 때문입니다.
Pegasus Epsilon


1

크기와 개수에 대해 별도의 인수를 갖는 것은 부분 레코드 읽기를 피할 수있는 구현에 유리할 수 있습니다. 파이프와 같은 것에서 단일 바이트 읽기를 사용하는 경우 고정 형식 데이터를 사용하더라도 레코드가 두 읽기로 분할 될 가능성을 허용해야합니다. 대신 293 바이트를 사용할 수있을 때 각각 10 바이트 씩 최대 40 개의 레코드에 대한 비 차단 읽기를 요청하고 시스템이 290 바이트 (29 개의 전체 레코드)를 반환하고 다음 읽기를 위해 3 바이트를 준비하도록 할 수 있다면 훨씬 더 편리합니다.

fread의 구현이 그러한 의미를 어느 정도 처리 할 수 ​​있는지는 알 수 없지만 지원을 약속 할 수있는 구현에는 확실히 유용 할 수 있습니다.


@PegasusEpsilon : 예를 들어 프로그램이 실행 fread(buffer, 10000, 2, stdin)되고 사용자가 18,000 바이트를 입력 한 후 newline-ctrl-D를 입력하면 함수가 처음 10,000 바이트를 반환하고 나머지 8,000 바이트는 향후 더 작은 읽기 요청을 위해 보류 상태로 둘 수 있으면 좋을 것입니다. 그것이 일어날 수있는 어떤 구현? 8,000 바이트는 향후 요청이있을 때까지 어디에 저장됩니까?
supercat

방금 테스트 한 결과 fread ()는 이와 관련하여 가장 편리한 방식으로 작동하지 않지만 짧은 읽기를 결정한 후 읽기 버퍼에 바이트를 다시 채우는 것은 예상보다 약간 더 많을 것입니다. 어쨌든 표준 라이브러리 기능. fread ()는 부분 레코드를 읽고 버퍼에 넣지 만 반환 값은 읽은 완전한 레코드 수를 지정 하고 stdin에서 가져온 짧은 읽기에 대해 아무것도 알려주지 않습니다.
Pegasus Epsilon

... 계속 ... 당신이 할 수있는 최선의 방법은 아마도 fread 전에 널로 읽기 버퍼를 채우고, fread ()가 널이 아닌 바이트에 대해 완료되었다고 말한 후에 레코드를 확인하는 것입니다. 레코드에 null이 포함될 수있는 경우 특별히 도움이되지는 않지만 size1보다 큰 값 을 사용하려는 경우 음 ... 레코드의 경우 ioctls 또는 기타 넌센스가있을 수 있습니다. 스트림에 적용하여 만들 수 있습니다. 다르게 행동하지만 깊이 탐구하지는 않았습니다.
Pegasus Epsilon

또한 부정확 함으로 인해 이전 댓글을 삭제했습니다. 오 잘.
Pegasus Epsilon

@PegasusEpsilon : C는 다양한 동작을 수용하는 많은 플랫폼에서 사용됩니다. 프로그래머가 모든 구현에서 동일한 기능을 사용하고 보장해야한다는 개념은 C의 가장 좋은 기능이었던 것을 무시합니다. 그 디자인은 프로그래머가 기능을 사용할 수 있고 사용 가능한 플랫폼에서 보장 할 수 있다는 것입니다. 어떤 종류의 스트림은 임의의 크기의 푸시 백을 쉽게 지원할 수 있으며, fread그러한 스트림에 대해 설명한대로 작업하는 것이 그러한 방식으로 작동하는 스트림을 식별 할 수있는 방법이 있다면 유용 할 것입니다.
supercat

0

C에 함수 과부하가 없기 때문이라고 생각합니다. 일부가 있으면 크기가 중복됩니다. 그러나 C에서는 배열 요소의 크기를 결정할 수 없으므로 하나를 지정해야합니다.

이걸 고려하세요:

int intArray[10];
fwrite(intArray, sizeof(int), 10, fd);

fwrite가 허용 된 바이트 수를 작성하면 다음과 같이 작성할 수 있습니다.

int intArray[10];
fwrite(intArray, sizeof(int)*10, fd);

그러나 그것은 비효율적입니다. sizeof (int) 배 더 많은 시스템 호출이 있습니다.

고려해야 할 또 다른 점은 일반적으로 배열 요소의 일부가 파일에 기록되는 것을 원하지 않는다는 것입니다. 정수를 원하거나 아무것도 원하지 않습니다. fwrite는 성공적으로 작성된 여러 요소를 반환합니다. 따라서 요소의 2 바이트 만 작성되었다는 사실을 발견하면 어떻게 하시겠습니까?

일부 시스템 (정렬로 인해)에서는 복사본을 만들고 이동하지 않고는 1 바이트의 정수에 액세스 할 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.