使用fread()进行部分读取,并在这里获取我期望的输出如何实现?

huangapple go评论57阅读模式
英文:

How would I use fread() for a partial read, and get my expected output here?

问题

I am having difficulty with using the fread() function to read values from a file for a very specific and unlikely use case. This sounds really stupid, but an assignment I am working on requires me to do a partial read of items in a file.

My file name is "test_file.txt", and it's written in the following format, with one integer per line:

23
4
10

My code snippet, which is meant to process the above three bytes at a time, looks like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main () {

    int value = 0;
    char buffer[3];
    FILE* channel_file = fopen("test_file.txt", "rb");
    
    while(fread(&buffer, sizeof(char), sizeof(buffer), channel_file) > 0) {

        sscanf(buffer, "%d", &value);
        printf("%d \n", value);
        memset(buffer, 0, sizeof(buffer));
    }
}

Since I am reading the file 3 bytes at a time, I was expecting it to look like this:

23\n
4\n1
0\n

which does seem to be the case (since \n counts as a byte). Printed, I wanted to see

23
41
0

However, when I try to print these values using the code above, I only get this:

23
4
英文:

I am having difficulty with using the fread() function to read values from a file for a very specific and unlikely use case. This sounds really stupid, but an assignment I am working on requires me to do a partial read of items in a file.

My file name is "test_file.txt", and it's written in the following format, with one integer per line:

23
4
10

My code snippet, which is meant to process the above three bytes at a time, looks like this:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

int main () {

    int value = 0;
    char buffer[3];
    FILE* channel_file = fopen(&quot;test_file.txt&quot;, &quot;rb&quot;);
    
    while(fread(&amp;buffer, sizeof(char), sizeof(buffer), channel_file) &gt; 0) {

        sscanf(buffer, &quot;%d&quot;, &amp;value);
        printf(&quot;%d \n&quot;, value);
        memset(buffer, 0, sizeof(buffer));
    }
}

Since I am reading the file 3 bytes at a time, I was expecting it to look like this:

23\n
4\n1
0\n

which does seem to be the case (since \n counts as a byte). Printed, I wanted to see

23
41
0

However, when I try to print these values using the code above, I only get this:

23
4

I have tried a variety of different parameters in the fread() function itself, but none of them give the expected output. I was hoping someone more familiar with C and string parsing could help.

** EDIT: Posted a solution below. The solution is prone to undefined behavior since buffer and str are not null terminated. A mechanism for adding null termination to the buffer and str char array would improve the solution here.

答案1

得分: 3

以下是翻译好的部分:

在审查下面的方法时,考虑到 OP 提供的文件输入为 &quot;23\n4\n10\n&quot;,打印出:

23
4
10

这不是 OP 的目标:

23
41
0

看起来我误解了 OP 的目标。
为了参考制作答案,请参阅维基


似乎 OP 必须使用 fread() 来进行输入。哎。

  • 考虑使用 fread() 读取 最多 这么多字符。在这种情况下,可能是最多 3 个。

  • 文本 模式打开文件以处理使用 &quot;\r\n&quot; 作为单个 &#39;\n&#39; 的系统。fopen(&quot;test_file.txt&quot;, &quot;rb&quot;) --> fopen(&quot;test_file.txt&quot;, &quot;r&quot;)

  • 使缓冲区变大一个,以便我们可以附加一个 空字符 并将 buffer 处理为 字符串sscanf(buffer... 预期一个 字符串

  • 不需要 &amp;bufferbuffer 本身将转换为 &amp;buffer[0] 的类型。

  • 使用 &quot; %n&quot; 存储转换字符串为 int 时消耗的字符数。使用这个值来调整下一个 fread() 中读取的字符数。

  • 在打印 &#39;\n&#39; 前不需要空格。

  • 良好的做法:检查 sscanf() 的返回值。

   // 未经测试的示意性代码。

    #define BUFFER_MAX_LEN 3
    char buffer[BUFFER_MAX_LEN + 1];
    size_t buffer_len = 0;  // 缓冲区中未处理字符的计数。
    size_t buffer_read;

    do {
      buffer_read = fread(&amp;buffer[buffer_len], sizeof buffer[0], 
          BUFFER_MAX_LEN - buffer_len, channel_file);
      buffer_len += buffer_read;
      buffer[buffer_len] = &#39;\0&#39;;  // 使 buffer[] 成为字符串
      int n;
      int conversion_count = sscanf(buffer, &quot;%d %n&quot;, &amp;value, &amp;n);
      if (conversion_count != 1) {
        printf(&quot;无法从 &lt;%s&gt; 转换。退出\n&quot;, buffer);
        break;
      }
      printf(&quot;%d\n&quot;, value);
      buffer_len -= n;
      // 将未使用的尾部字符移动到前面。
      memmove(buffer, buffer + n, buffer_len);
    } while (buffer_read &gt; 0);

  • 更高级的方法将使用一个更大的缓冲区并查找字符串中的多个 int。然后,我们可以减少冗余的 memmove()

  • 使用 strtol() 要比 sscanf(...&quot;%d ... 更健壮。

将这些留到以后。

英文:

On review, the approach below, given OP's file input of &quot;23\n4\n10\n&quot;, prints:

23
4
10

This is not OP's goal of:

23
41
0

Seems I mis-understood OP's goal.
Making answer wiki for reference.


It appears OP is obliged to use fread() for input. Sigh.

  • Consider using fread() to read in up to so many characters. Maybe up to 3 in this case.

  • Open the file in text mode to handle systems that use &quot;\r\n&quot; as a single &#39;\n&#39;. fopen(&quot;test_file.txt&quot;, &quot;rb&quot;) --> fopen(&quot;test_file.txt&quot;, &quot;r&quot;).

  • Make the buffer one larger so we can append a null character and process buffer as a string. sscanf(buffer... expects a string.

  • No need for &amp;buffer. buffer itself will convert to the type of &amp;buffer[0].

  • Use &quot; %n&quot; to store how many characters were consumed converting the string into an int. Use that to adjust how many characters to read in the next fread().

  • No need for a space before printing a &#39;\n&#39;.

  • Good practice: check return value from sscanf().

   // Untested illustrative code.

    #define BUFFER_MAX_LEN 3
    char buffer[BUFFER_MAX_LEN + 1];
    size_t buffer_len = 0;  // Count of unprocessed characters in the buffer.
    size_t buffer_read;

    do {
      buffer_read = fread(&amp;buffer[buffer_len], sizeof buffer[0], 
          BUFFER_MAX_LEN - buffer_len, channel_file);
      buffer_len += buffer_read;
      buffer[buffer_len] = &#39;
   // Untested illustrative code.
#define BUFFER_MAX_LEN 3
char buffer[BUFFER_MAX_LEN + 1];
size_t buffer_len = 0;  // Count of unprocessed characters in the buffer.
size_t buffer_read;
do {
buffer_read = fread(&amp;buffer[buffer_len], sizeof buffer[0], 
BUFFER_MAX_LEN - buffer_len, channel_file);
buffer_len += buffer_read;
buffer[buffer_len] = &#39;\0&#39;;  // Make buffer[] a string
int n;
int conversion_count = sscanf(buffer, &quot;%d %n&quot;, &amp;value, &amp;n);
if (conversion_count != 1) {
printf(&quot;Nothing to convert from &lt;%s&gt;. Quitting\n&quot;, buffer);
break;
}
printf(&quot;%d\n&quot;, value);
buffer_len -= n;
// Move unused trailing characters to the front.
memmove(buffer, buffer + n, buffer_len);
} while (buffer_read &gt; 0);
&#39;; // Make buffer[] a string int n; int conversion_count = sscanf(buffer, &quot;%d %n&quot;, &amp;value, &amp;n); if (conversion_count != 1) { printf(&quot;Nothing to convert from &lt;%s&gt;. Quitting\n&quot;, buffer); break; } printf(&quot;%d\n&quot;, value); buffer_len -= n; // Move unused trailing characters to the front. memmove(buffer, buffer + n, buffer_len); } while (buffer_read &gt; 0);

  • A more advanced approach would use a much larger buffer and look for multiple int in the string. We could then reduce the wasteful memmove().

  • It is more robust to use strtol() than sscanf(...&quot;%d ...

Leave those for a later day.

答案2

得分: 1

You seem to have made a very simple thing rather complicated. What you appear to require from your description is to:

> read the file three bytes at a time and to print only the digit characters from those three bytes on a new line.

A simple and clear statement of requirements leads to a simpler solution. I wonder what the actual stated requirements were in the original assignment?

Given the stated precondition that the file contains only digits and newlines, then there is no need even to convert anything to an integer. Neither is there any need for complex string manipulation or string termination.

#include &lt;stdio.h&gt;

int main() 
{
    FILE* channel_file = fopen( &quot;test_file.txt&quot;, &quot;r&quot;);
    
    if( channel_file != NULL )
    {
        size_t count = 0 ;
        char buffer[3] ;

        while( 0 &lt; (count = fread(&amp;buffer, 1u, sizeof(buffer), channel_file)) ) 
        {
            for( size_t c = 0; c &lt; count; c++ )
            {
                if( buffer[c] != &#39;\n&#39; )
                {
                    putchar( buffer[c] ) ;
                }
            }
            putchar( &#39;\n&#39; ) ;
        }
    }

    return 0 ;
}

Noting also that sizeof(char) is 1 by definition. It is not wrong, but not necessary to ask the size of a char.

A "safer" method that does not rely on the file containing only digits and newline is to #include &lt;ctype.h&gt; then then change the digit output condition to:

                if( isdigit( buffer[c] ) )
                {
                    putchar( buffer[c] ) ;
                }

If on the other hand the assignment requires you to determine an integer value, then the while loop body can be changed to:

            unsigned value = 0 ;
            for( int c = 0; c &lt; count; c++ )
            {
                if( isdigit( buffer[c] ) )
                {
                    value *= 10 ;
                    value +=  buffer[c] - &#39;0&#39; ;
                }
            }
            printf( &quot;%u\n&quot;, value ) ;

avoiding any building of a string in order to perform the conversion.

英文:

You seem to have made a very simple thing rather complicated. What you appear to require from your description is to:

> read the file three bytes at a time and to print only the digit characters from those three bytes on a new line.

A simple and clear statement of requirements leads to a simpler solution. I wonder what the actual stated requirements were in the original assignment?

Given the stated precondition that the file contains only digits and newlines, then there is no need even to convert anything to an integer. Neither is there any need for complex string manipulation or string termination.

#include &lt;stdio.h&gt;

int main() 
{
    FILE* channel_file = fopen( &quot;test_file.txt&quot;, &quot;r&quot;);
    
    if( channel_file != NULL )
    {
        size_t count = 0 ;
        char buffer[3] ;

        while( 0 &lt; (count = fread(&amp;buffer, 1u, sizeof(buffer), channel_file)) ) 
        {
            for( size_t c = 0; c &lt; count; c++ )
            {
                if( buffer[c] != &#39;\n&#39; )
                {
                    putchar( buffer[c] ) ;
                }
            }
            putchar( &#39;\n&#39; ) ;
        }
    }

    return 0 ;
}

Noting also that sizeof(char) is 1 by definition. It is not wrong, but not necessary to ask the size of a char.

A "safer" method that does not rely on the file containing only digits and newline is to #include &lt;ctype.h&gt; then then change the digit output condition to:

                if( isdigit( buffer[c] ) )
                {
                    putchar( buffer[c] ) ;
                }

If on the other hand the assignment requires you to determine an integer value, then the while loop body can be changed to:

            unsigned value = 0 ;
            for( int c = 0; c &lt; count; c++ )
            {
                if( isdigit( buffer[c] ) )
                {
                    value *= 10 ;
                    value +=  buffer[c] - &#39;0&#39; ;
                }
            }
            printf( &quot;%u\n&quot;, value ) ;

avoiding any building of a string in order to perform the conversion.

huangapple
  • 本文由 发表于 2023年7月13日 12:32:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76675957.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定