开始学习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函数,但创建了一个无限循环,却不知道如何做。抱歉,我不确定要问什么样的问题。
答案 0 :(得分:1)
Netpbm格式有点奇怪,因为许多程序员最初会错误地读取它们。
简而言之,“幻数”(P1
至P7
)必须位于文件的开头,然后是标题字段,然后是单个空格字符,然后是数据。诀窍在于,每个标头字段都可以在前加上空格和/或注释,并且标头后跟一个空格字符。
P7
格式(可移植任意映射)文件已命名了标头字段,但是无论如何它几乎不受支持,因此我将限于常见的P1
至P6
格式。 (不过,要支持其标题字段,您只需要另一个帮助器函数即可。)
您需要四个助手功能:
将十进制数字转换为其数值的函数。
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兼容字符集的机器上,它们是现今的,非常罕见。)
读取幻数的功能。
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 */
}
}
当然,并不是绝对需要使用辅助函数来解析幻数,但是绝对可以使用一个辅助函数使您的代码更易于阅读,验证和维护。所以使用一个是一件好事。
用于读取标头末尾空白字符的函数。
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,如果发生错误则返回非零。
用于解析标头字段值(非负整数)的函数。
请注意,此函数会跳过前导空格和注释,但会将终止值的字符留在流中(通过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. */
}
}
记住:
P1
和P4
格式具有两个标头字段: width 和 height (按此顺序)。
P2
,P3
,P5
和P6
格式具有三个标头字段: width , height < / em>和 maxval 。
您可以使用fscanf(handle, "%u", &value)
来读取P1
和P2
格式文件中的每个像素(假定为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);
}