读取ppm标头信息,并输出到C语言的控制台和输出文件

时间:2018-10-14 17:51:00

标签: c ppm

开始学习C;我正在尝试阅读和处理字符,同时丢弃空白字符。我还需要确定它是注释“#”还是下一个输入值的第一个字符。目的是获取幻数,宽度,高度和maxval并将其写入新文件。

public IActionResult PickInfo(Info obj, int? SelectedAlertIndex)
    {
        if (SelectedAlertIndex.HasValue)
        {
            ViewBag.Message = "Info loaded successfully";
        }
        return View(_context.Info.Where(x => x.InfoIndex == SelectedAlertIndex));
    }

Edit :利用了建议的isspace函数,但创建了一个无限循环,却不知道如何做。抱歉,我不确定要问什么样的问题。

2 个答案:

答案 0 :(得分:1)

Netpbm格式有点奇怪,因为许多程序员最初会错误地读取它们。

简而言之,“幻数”(P1P7)必须位于文件的开头,然后是标题字段,然后是单个空格字符,然后是数据。诀窍在于,每个标头字段都可以在加上空格和/或注释,并且标头后跟一个空格字符。

P7格式(可移植任意映射)文件已命名了标头字段,但是无论如何它几乎不受支持,因此我将限于常见的P1P6格式。 (不过,要支持其标题字段,您只需要另一个帮助器函数即可。)

您需要四个助手功能:

  1. 将十进制数字转换为其数值的函数。

    static int  decimal_digit(const int c)
    {
        switch (c) {
        case '0': return 0;
        case '1': return 1;
        case '2': return 2;
        case '3': return 3;
        case '4': return 4;
        case '5': return 5;
        case '6': return 6;
        case '7': return 7;
        case '8': return 8;
        case '9': return 9;
        default: return -1;
        }
    }
    

    通常,您会看到它缩写为(c - '0')(或((c >= '0' && c <= '9') ? (c - '0') : -1),如果您想要与上述功能等效的表达式),但是只有在操作系统使用以下字符集的情况下,该方法才有效:十进制数字是连续的代码点。 (除了在使用EBCDIC或某些其他非ASCII兼容字符集的机器上,它们是现今的,非常罕见。)

  2. 读取幻数的功能。

    int  pnm_magic(FILE *in)
    {
        int  c;
    
        if (!in || ferror(in))
            return -2;  /* Invalid file handle. */
    
        c = getc(in);
        if (c != 'P')
            return -1;  /* Not a NetPBM file. */
    
        switch (getc(in)) {
        case '1': return 1;  /* Ascii PBM */
        case '2': return 2;  /* Ascii PGM */
        case '3': return 3;  /* Ascii PPM */
        case '4': return 4;  /* Binary PBM */
        case '5': return 5;  /* Binary PGM */
        case '6': return 6;  /* Binary PPM */
        /* case '7': return 7; for Portable Arbitrary map file */
        default: return -1;  /* Unknown format */ 
        }
    }
    

    当然,并不是绝对需要使用辅助函数来解析幻数,但是绝对可以使用一个辅助函数使您的代码更易于阅读,验证和维护。所以使用一个是一件好事。

  3. 用于读取标头末尾空白字符的函数。

    int  pnm_endheader(FILE *in)
    {
        int  c;
    
        if (!in || ferror(in))
            return -1;  /* Invalid file handle. */
    
        c = getc(in);
    
        /* Whitespace? */
        if (c == '\t' || c == '\n' || c == '\v' ||
            c == '\f' || c == '\r' || c == ' ')
            return 0;
    
        /* Nope, error. Don't consume the bad character. */
        if (c != EOF)
            ungetc(c, in);
    
        return -1;
    }
    

    请注意,如果成功,此函数将返回0,如果发生错误则返回非零。

  4. 用于解析标头字段值(非负整数)的函数。

    请注意,此函数会跳过前导空格和注释,但会将终止值的字符留在流中(通过ungetc())。

    int  pnm_value(FILE *in)
    {
        unsigned int  val, old;
        int           c, digit;
    
        if (!in || ferror(in))
            return -1;  /* Invalid file handle. */
    
        /* Skip leading ASCII whitespace and comments. */
        c = getc(in);
        while (c == '\t' || c == '\n' || c == '\v' ||
               c == '\f' || c == '\r' || c == ' ' || c == '#')
            if (c == '#') {
                /* Skip the rest of the comment */
                while (c != EOF && c != '\n' && c != '\r')
                    c = getc(in);
            } else
                c = getc(in);
    
        /* Parse initial decimal digit of value. */
        val = decimal_digit(c);
        if (val < 0)
            return -2; /* Invalid input. */
    
        while (1) {
            c = getc(in);
    
            /* Delimiter? End of input? */
            if (c == '\t' || c == '\n' || c == '\v' ||
                c == '\f' || c == '\r' || c == ' ' || c == '#') {
                /* Do not consume the character following the value. */
                ungetc(c, in);
                return val;
            } else
            if (c == EOF)
                return val;
    
            /* Is it a decimal digit? */
            digit = decimal_digit(c);
            if (digit < 0)
                return -2; /* Invalid input. */
    
            /* Convert, checking for overflow. */
            old = val;
            val = (val * 10) + digit;
            if (val / 10 != old)
                return -3; /* Overflow. */
        }
    }
    

记住:

  • P1P4格式具有两个标头字段: width height (按此顺序)。

    < / li>
  • P2P3P5P6格式具有三个标头字段: width height < / em>和 maxval

  • 您可以使用fscanf(handle, "%u", &value)来读取P1P2格式文件中的每个像素(假定为unsigned int value;)。如果成功,它将返回1。对于P1,值将为0或1;对于P2,它将是从0到 maxval (含)。

  • 您可以使用fscanf(handle, "%u %u %u", &red, &green, &blue)来读取P3格式文件中的每个像素(假定为unsigned int red, green, blue;)。如果成功,它将返回3。然后,每个组成部分将从0到 maxval (包括两端)。

  • P4格式最讨厌阅读。最好使用fread(buf, width, 1, handle)unsigned char buf[width];或类似大小的动态分配数组一次完成一行像素。然后,像素x!!(buf[x/8] & (1 << (x & 7)))(0为白色,1为黑色; x 从0到 width -1)。 (!!是一个双非或非非运算符:如果参数为0,则返回0,否则为1。)

  • 对于P5格式,如果 maxval > = 256,则每个像素由两个字节组成。您可以使用

    static float p5_gray(FILE *in, int maxval)
    {
        if (maxval >= 256 && maxval < 65536) {
            int  hi, lo;
            hi = fgetc(in);
            lo = fgetc(in);
            if (lo == EOF)
                return -1.0f;
            return (float)(hi*256 + lo) / (float)maxval;
        } else
        if (maxval >= 1 && maxval < 256) {
            int  val;
            val = fgetc(in);
            if (val == EOF)
                return -1.0f;
            return (float)val / (float)maxval;
        } else
            return -2.0f;
    }
    

    P5格式读取每个像素。该函数为白色返回0.0f,为黑色返回1.0f。

  • 对于P6格式,如果 maxval > = 256,则每个像素为6个字节;否则,每个像素为三个字节。您可以使用例如

    static int p6_rgb(FILE *in, int maxval, float *red, float *green, float *blue)
    {
        const float    max = (float)maxval;
        unsigned char  buf[6];
    
        if (maxval >= 256 && maxval < 65536) {
            if (fread(buf, 6, 1, in) != 1)
                return -1; /* Error! */
            if (red)
                *red = (float)(buf[0]*256 + buf[1]) / max;
            if (green)
                *green = (float)(buf[2]*256 + buf[1]) / max;
            if (blue)
                *blue = (float)(buf[4]*256 + buf[5]) / max;
            return 0;
        } else
        if (maxval >= 1 && maxval < 256) {
            if (fread(buf, 3, 1, in) != 1)
                return -1; /* Error! */
            if (red)
                *red = (float)buf[0] / max;
            if (green)
                *green = (float)buf[1] / max;
            if (blue)
                *blue = (float)buf[2] / max;
            return 0;
        } else
            return -2; /* Invalid maxval */
    }
    

    P6格式的文件中读取每个像素。

因此,如果in是打开的文件句柄(或说stdin),并且您有int format, width, height, maxval;,则可以这样做

format = pnm_magic(in);
if (format < 1 || format > 6) {
    /* Unrecognized format; fail! */
}

width = pnm_value(in);
if (width <= 0) {
    /* Invalid width; fail! */
}

height = pnm_value(in);
if (height <= 0) {
    /* Invalid height; fail! */
}

if (format == 2 || format == 3 || format == 5 || format == 6) {
    maxval = pnm_value(in);
    if (maxval < 1 || maxval > 65535) {
        /* Invalid maxval; fail! */
    }
}

if (pnm_endheader(in)) {
    /* Bad end of header; fail! */
}

解析标头,将文件位置保留在像素数据的开头。

答案 1 :(得分:0)

for (int j = i; i != ' ' || i != '\n'; j = fgetc(input))

您使用的是依赖于i的表达式,但是您更改了j

然后在循环中执行:

buffer[num_chars++] = j;

最终会溢出。

可能您是说:

for (int j = i; j != ' ' || j != '\n'; j = fgetc(input))

但是为什么不使用isspace()之类的标准函数呢?

还要检查缓冲区溢出:

    for (int j = i; j != ' ' || j != '\n'; j = fgetc(input)){
        assert(num_chars < sizeof(buffer)/sizeof(*buffer);
        buffer[num_chars++] = j;
        printf("Comment found: %s\n", j);
    }