C 程序设计语言第二版——第五章练习题
- 1. As written, getint treats a + or - not followed by a digit as a valid representation of zero. Fix it to push such a character back on the input.
- 2. Write getfloat, the floating-point analog of getint. What type does getfloat return as its function value?
- 3. Write a pointer version of the function strcat that we showed in Chapter 2: strcat(s,t) copies the string t to the end of s.
- 4. Write the function strend(s,t), which returns 1 if the string t occurs at the end of the string s, and zero otherwise.
- 5. Write versions of the library functions strncpy, strncat, and strncmp, which operate on at most the first n characters of their argument strings. For example, strncpy(s,t,n) copies at most n characters of t to s. Full descriptions are in Appendix B.
- 6. Rewrite appropriate programs from earlier chapters and exercises with pointers instead of array indexing. Good possibilities include getline (Chapters 1 and 4), atoi, itoa, and their variants (Chapters 2, 3, and 4), reverse (Chapter 3), and strindex and getop (Chapter 4).
- 8 There is no error checking in day_of_year or month_day. Remedy this defect.
- 9. Rewrite the routines day_of_year and month_day with pointers instead of indexing.
- 10. Write the program expr, which evaluates a reverse Polish expression from the command line, where each operator or operand is a separate argument. For example,
- 11. Modify the program entab and detab (written as exercises in Chapter 1) to accept a list of tab stops as arguments. Use the default tab settings if there are no arguments.
- 12. Extend entab and detab to accept the shorthand
- 13. Write the program tail, which prints the last n lines of its input. By default, n is set to 10, let us say, but it can be changed by an optional argument so that *tail -n* prints the last n lines.
- 14. Modify the sort program to handle a -r flag, which indicates sorting in reverse (decreasing) order. Be sure that -r works with -n.
- 15. Add the option -f to fold upper and lower case together, so that case distinctions are not made during sorting; for example, a and A compare equal.
- 16. Add the -d (``directory order'') option, which makes comparisons only on letters, numbers and blanks. Make sure it works in conjunction with -f.
- 17. Add a field-searching capability, so sorting may bee done on fields within lines, each field sorted according to an independent set of options. (The index for this book was sorted with -df for the index category and -n for the page numbers.)
- ?18. Make dcl recover from input errors.
- ?19. Modify undcl so that it does not add redundant parentheses to declarations.
- ?20. Expand dcl to handle declarations with function argument types, qualifiers like const, and so on.
1. As written, getint treats a + or - not followed by a digit as a valid representation of zero. Fix it to push such a character back on the input.
分析:
感觉这个函数功能就跟 scanf () 输入 int一样。
#include<stdio.h>
#include<ctype.h>
#define SIZE 100
int getch(void);
void ungetch(int);
int getint(int *pn);
int main(void)
{
int a[SIZE], n;
if (getint(&n) > 0)
printf("n = %d\n",n);
else
printf("Not an integer.\n");
return 0;
}
//getch() ungetch()
#define BUFSIZE 10
int bufp = 0;
char buf[BUFSIZE];
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int n)
{
if (bufp >= BUFSIZE)
printf("Error: too many characters.\n");
else
buf[bufp++] = n;
}
//getint()
int getint(int *pn)
{
int c, c1, i, sign;
while (isspace(c = getch()))
;
if (!isdigit(c) && c != '+' && c != '-' && c != EOF) {
ungetch(c);
return 0;
}
if (c == EOF)
return c;
sign = (c == '-') ? -1 : 1;
if (c == '-' || c == '+')
c1 = getch();
for (*pn = 0, i = 0; isdigit(c1); c1 = getch(), i++)
*pn = 10 * (*pn) + c1 - '0';
if (i == 0) {
ungetch(c);
ungetch(c1);
return 0;
}
*pn = *pn * sign;
if (c1 != EOF)
ungetch(c1);
return c1;
}
输出:
-0932.3d
n = -932
2. Write getfloat, the floating-point analog of getint. What type does getfloat return as its function value?
分析:
觉得这个函数就跟 atof() 函数一个功能,将输入的浮点字符转换为浮点数。
正确识别返回 1,非法字符返回 0, 遇到 EOF 返回 EOF。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
int getch(void);
void ungetch(int);
int getfloat(double *pn);
int main(void)
{
double n;
if (getfloat(&n) == 1)
printf("n = %.8g\n", n);
else
puts("Not a number");
return 0;
}
//getch() ungetch()
#define BUFSIZE 40
int bufp = 0;
char buf[BUFSIZE];
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE)
printf("Error: too many characters.\n");
else
buf[bufp++] = c;
}
// getfloat()
int getfloat(double *pn)
{
int sign, sign_e;
int hasint = 0; //判断小数点前是否有数字
int hassign = 0;//判断是否有符号
double intpart = 0.0;//小数的整数部分
double decpart = 0.0;//小数的小数部分
int exp = 0;//指数部分
int decdigit = 1;//小数的小数点后数字位置
int c, c1, c2;
while (isspace(c = getch()))
;
if (!isdigit(c) && c != '-' && c != '+' && c != '.' && c != EOF) {
ungetch(c);
return 0;
}
// if (c == EOF)
// return c;
sign = (c == '-') ? -1 : 1;
if (c == '-' || c == '+') {
c1 = getch();
if (!isdigit(c1) && c1 != '.' && c1 != EOF){
ungetch(c);
ungetch(c1);
return 0;
}
c = c1;
hassign = 1;
} // c 只能是数字或点号, EOF
//如果 c 是数字,计算小数的整数部分
for (intpart = 0.0; isdigit(c); c = getch()) {
intpart = 10 * intpart + c - '0';
hasint = 1;
}
// c 可能是非法字符,科学计数符 e 或 E, 小数点, EOF
//如果是非法字符,那已经扫描到数字
if (c != '.' && c != 'e' && c != 'E' && c != EOF) {
ungetch(c);
*pn = sign * intpart;
return 1;
}
//如果是小数点
if (c == '.') {
c1 = getch();
//如果小数点前面没数字,后面也没数字,则非法
if (hasint == 0 && !isdigit(c1)) {
if (hassign == 1) {
c2 = (sign == -1) ? '-' : '+';
ungetch(c2);
}
ungetch(c);
ungetch(c1);
return 0;
}
//小数点前有数字,后面非法
else if (hasint == 1 && !isdigit(c1) && c1 != 'e' && c1 != 'E') {
if (c1 != EOF)
ungetch(c1);
*pn = sign * intpart;
return 1;
}
//小数点后数字或者e(小数点前有数字)
//计算小数部分
for (decpart = 0.0; isdigit(c1); c1 = getch())
decpart = (c1 - '0') / pow(10, decdigit++);
c = c1;
}
//小数部分计算完成
//如果扫描到 e
hassign = 0;
if (c == 'e' || c == 'E') {
if ((c1 = getch()) == '-' || c1 == '+')
hassign = 1;
sign_e = (c == '-') ? -1 : 1;
c2 = getch();
if (!isdigit(c2)) {
if (hassign == 1) {
ungetch(c);
ungetch((sign_e == -1) ? '-' : '+');
}
if (c2 != EOF)
ungetch(c2);
*pn = sign * (intpart + decpart);
return 1;
}
c = c2;
}
// 计算指数部分
for (exp = 0; isdigit(c); c = getch())
exp = exp * 10 + c - '0';
if (c != EOF)
ungetch(c);
exp = sign_e * exp;
*pn = sign * (intpart + decpart) * pow(10, exp);
return 1;
}
输出:
-.3e2
n = -0.3
3. Write a pointer version of the function strcat that we showed in Chapter 2: strcat(s,t) copies the string t to the end of s.
分析:
添加一个参数 n 用于说明连接的最大字符串个数,检查是否超出范围。
#include<stdio.h>
#include<string.h>
#define SIZE 100
#define LEN 40
int input(int min, int max);
char *s_gets(char *s, int n);
char *mystrncat(char *s, char *t, int n);
int main(void)
{
char s[SIZE], t[LEN];
int len1, len2, maxlen, n;
printf("Please enter the source string:\n");
s_gets(s, SIZE);
printf("Please enter the target string:\n");
s_gets(t, LEN);
len1 = strlen(s);
len2 = strlen(t);
maxlen = (SIZE - len1 -1) > len2 ? len2 : SIZE - len1 -1;
printf("Please enter the numbers of characters to cat (0 ~ %d): ",maxlen);
n = input(0, maxlen);
mystrncat(s, t, n);
printf("New string:\n%s\n",s);
return 0;
}
char *s_gets(char *s, int n)
{
char *ret, *find;
ret = fgets(s, n, stdin);
if (ret) {
find = strchr(s, '\n');
if (find)
*find = '\0';
else
while (getchar() != '\n')
continue;
}
return ret;
}
int input(int min, int max)
{
int n, status;
int ok = 1;
while (ok) {
status = scanf("%d", &n);
if (status != 1 || n < min || n > max || getchar() != '\n' ) {
printf("Enter again: ");
while (getchar() != '\n')
continue;
continue;
}
else
return n;
}
}
char *mystrncat(char *s, char *t, int n)
{
int i, j;
for (i = strlen(s), j = 0; j < n; *(s+i) = *(t+j),i++, j++)
;
if (n < strlen(t))
*(s+i) = '\0';
return s;
}
4. Write the function strend(s,t), which returns 1 if the string t occurs at the end of the string s, and zero otherwise.
#include<stdio.h>
#include<string.h>
#define LEN 50
char *s_gets(char *s, int n);
int strend(char *s, char *t);
int main(void)
{
char s[LEN], t[LEN];
int n;
printf("Please enter the source string:\n");
s_gets(s, LEN);
printf("Please enter the target string:\n");
s_gets(t, LEN);
n = strend(s, t);
if (n == 1)
printf("%s is at the end of %s.\n", t, s);
else
printf("%s is not at the end of %s.\n", t, s);
return 0;
}
char *s_gets(char *s, int n)
{
char *ret, *find;
ret = fgets(s, n, stdin);
if (ret) {
find = strchr(s, '\n');
if (find)
*find = '\0';
else
while (getchar() != '\n')
continue;
}
return ret;
}
int strend(char *s , char *t)
{
int len1 = strlen(s);
int len2 = strlen(t);
if (len2 > len1)
return 0;
int i, j;
for (i = len1-len2, j = 0; (j < len2) && (s[i] == t[j]); i++, j++)
;
if (j != len2)
return 0;
else
return 1;
}
5. Write versions of the library functions strncpy, strncat, and strncmp, which operate on at most the first n characters of their argument strings. For example, strncpy(s,t,n) copies at most n characters of t to s. Full descriptions are in Appendix B.
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define LEN 100
char *s_gets(char *s , int n);
int input(int min, int max);
char getlet(char *s);
void menu(void);
char *mstrncpy(char *s, char *t, int n);
char *mstrncat(char *s, char *t, int n);
int mstrncmp(char *s, char *t, int n);
int main(void)
{
char s[LEN], t[LEN];
int n;
char c;
printf("Please enter the source string:\n");
s_gets(s, LEN);
printf("Please enter the target string:\n");
s_gets(t, LEN);
printf("Please enter the number of characters to be processed: ");
n = input(0, LEN-1);
menu();
c = getlet("abc");
switch(c) {
case 'a': mstrncpy(s, t, n); break;
case 'b': mstrncat(s, t, n); break;
case 'c': mstrncmp(s, t, n); break;
}
return 0;
}
char *s_gets(char *s, int n)
{
char *ret, *find;
ret = fgets(s, n, stdin);
if (ret) {
find = strchr(s, '\n');
if (find)
*find = '\0';
else
while (getchar() != '\n')
continue;
}
return ret;
}
int input(int min, int max)
{
int n, status;
int ok = 1;
while (ok) {
status = scanf("%d", &n);
if (status != 1 || n < min || n > max || getchar() != '\n') {
while (getchar() != '\n')
continue;
printf("Enter again (%d ~ %d): ", min, max);
continue;
}
return n;
}
}
void menu(void)
{
puts("Choose your choice:");
puts("a) strncpy b) strncat");
puts("c) strncmp");
}
char getlet(char *s)
{
char c;
int ok = 1;
while (ok) {
c = getchar();
c = tolower(c);
if (strchr(s, c))
return c;
printf("Enter again: ");
}
}
char *mstrncpy(char *s, char *t, int n)
{
int len, i;
len = strlen(t);
for (i = 0; i < n && t[i] != '\0'; i++)
s[i] = t[i];
s[i] = '\0';
printf("Source string: %s\n",s);
return s;
}
char *mstrncat(char *s, char *t, int n)
{
int len1, len2, maxlen;
int i, j;
len1 = strlen(s);
len2 = strlen(t);
n = (n > len2) ? len2 : n;
maxlen = LEN - len1 - 1;
if (n > maxlen) {
printf("Beyond the range, enter the number of characters to be"
" catenated (0 ~ %d) again: ",maxlen);
n = input(0, maxlen);
}
for (i = len1, j = 0; j < n; s[i] = t[j], i++, j++)
;
s[i] = '\0';
printf("Source string: %s\n",s);
return s;
}
int mstrncmp(char *s, char *t, int n)
{
int i;
for (i = 0; i < n && s[i] == t[i]; i++)
;
if (i == n) {
printf("The first %d characters of %s and %s are the"
" same.\n",n,s,t);
return 0;
}
else if (s[i] > t[i])
printf("%s precede %s alphabetically.\n",t,s);
else
printf("%s precede %s alphabetically.\n",s,t);
return s[i] -t[i];
}
6. Rewrite appropriate programs from earlier chapters and exercises with pointers instead of array indexing. Good possibilities include getline (Chapters 1 and 4), atoi, itoa, and their variants (Chapters 2, 3, and 4), reverse (Chapter 3), and strindex and getop (Chapter 4).
将书中的指针数组改为二位数组,存放字符串内容而非地址。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXLINES 5000
#define MAXLEN 100
int mgetline(char *a, int n);
int readlines(char (*line)[MAXLEN], int maxlines);
void mqsort(char (*a)[MAXLEN], int left, int right);
void writelines(char (*a)[MAXLEN], int maxlines);
int main(void)
{
int nlines;
char line[MAXLINES][MAXLEN];
printf("Enter strings (empty line to stop):\n");
if ((nlines = readlines(line, MAXLINES)) > 0) {
mqsort(line, 0, nlines-1);
printf("\nSorted strings:\n");
writelines(line, nlines);
return 0;
}
else {
printf("Error: no input.\n");
return -1;
}
}
int mgetline(char *a, int n)
{
int i, c;
for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
*(a+i) = c;
*(a+i) = '\0';
return i;
}
int readlines(char line[][MAXLEN], int maxlines)
{
int nlines, len;
char a[MAXLEN];
for (nlines = 0; nlines < maxlines && (len = mgetline(a, MAXLEN-1)) > 0; nlines++)
strcpy(line[nlines], a);
return nlines;
}
//快速排序
#define cutoff 3
void swap(char (*s1)[MAXLEN], char (*s2)[MAXLEN])
{
char t[MAXLEN]; //这里交换字符串,是一个以为数组,不能直接赋值
strcpy(t, *s1);
strcpy(*s1, *s2);
strcpy(*s2, t);
}
char *Median3(char (*a)[MAXLEN], int left, int right)
{
int center = (left + right) / 2;
if (strcmp(a[left], a[center]) > 0)
swap(&a[left], &a[center]);
if (strcmp(a[left], a[right]) > 0)
swap(&a[left], &a[right]);
if (strcmp(a[center], a[right]) > 0)
swap(&a[center], &a[right]);
swap(&a[center], &a[right-1]); //pivot == a[right-1]
return a[right-1];
}
void InsertionSort(char (*a)[MAXLEN], int nlines)
{
int i, p;
char tmp[MAXLEN];
for (p = 1; p < nlines; p++) {
strcpy(tmp, a[p]);
for (i = p; i > 0 && strcmp(a[i-1], tmp) > 0; i--)
strcpy(a[i], a[i-1]);
strcpy(a[i], tmp);
}
}
void mqsort(char (*a)[MAXLEN], int left, int right)
{
int i, j;
if (left + cutoff <= right) {
char *pivot;
pivot = Median3(a, left, right);
i = left;
j = right - 1;
for (; ; ) {
while (strcmp(a[++i], pivot) < 0)
;
while (strcmp(a[--j], pivot) > 0)
;
if (i < j)
swap(&a[i], &a[j]);
else
break;
}
swap(&a[i], &a[right-1]);
mqsort(a, left, i-1);
mqsort(a, i+1, right);
}
else
InsertionSort(a, right - left + 1);
}
//writelines
void writelines(char (*a)[MAXLEN], int maxlines)
{
int i;
for (i = 0; i < maxlines; i++)
puts(a[i]);
}
输出:
Enter strings (empty line to stop):
sja
d
ddj
ds
cj
Sorted strings:
cj
d
ddj
ds
sja
8 There is no error checking in day_of_year or month_day. Remedy this defect.
需要实现功能是:
输入年,月,日,计算该日是该年的第几天。
输入年,这一年的第几天,计算月份和该月的天数。
输入超过范围时提示错误并重新输入。
#include<stdio.h>
#include<limits.h>
#include<string.h>
char day_tab[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
int input(int min, int max);
int day_of_year(int leap, int month, int m_day);
void month_day(int leap, int day, int *pmonth, int *m_day);
int main(void)
{
int year, month, m_day, day;
int leap;
int day_max;
printf("Please enter the year: ");
year = input(1, INT_MAX);
printf("Enter the month: ");
month = input(1, 12);
leap = year%4 == 0 && year/100 != 0 || year%400 == 0;//true for leap year
day_max = day_tab[leap][month];
printf("Now enter the month day (1 ~ %d): ",day_max);
m_day = input(1, day_max);
day = day_of_year(leap, month, m_day);
printf("\nThe day %d-%d-%d is %d day of %d.\n", year,month,m_day,day,year);
printf("\nEnter the year: ");
year = input(1, INT_MAX);
leap = year%4 == 0 && year/100 != 0 || year%400 == 0;//true for leap year
day_max = (leap == 0) ? 365 : 366;
printf("Enter the day of year: ");
day = input(1, day_max);
month_day(leap, day, &month, &m_day);
printf("year: %d, day of year: %d, month: %d, day of month: "
"%d\n",year,day,month,m_day);
return 0;
}
int input(int min, int max)
{
int status;
long int n;
int ok = 1;
while (ok) {
status = scanf("%ld",&n);
if (status != 1 && n < min && n > max && getchar() != '\n') {
printf("Enter again (%d ~ %d): ",min, max);
while (getchar() != '\n')
continue;
continue;
}
else
return (int)n;
}
}
int day_of_year(int leap, int month, int m_day)
{
int i;
int day = m_day;
for (i = 1; i < month; i++)
day += day_tab[leap][i];
return day;
}
void month_day(int leap, int day, int *pmonth, int *pm_day)
{
int i, j;
for (i = 1; (day - day_tab[leap][i]) > 0; i++)
day -= day_tab[leap][i];
*pmonth = i;
*pm_day = day;
}
9. Rewrite the routines day_of_year and month_day with pointers instead of indexing.
分析:原来用二位数组存放月份天数:
char day_tab[2][13];
两行分别表示平年和闰年的月份,使用时操作数组行列。
现在,用指针操作,只需要一个一维数组存放每月的天数,而平年和闰年通过一个一个指针修改二月份天数。因此变成:
char month_tab[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char *day_tab = month_tab;
判断是否闰年:
leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
*(day_tab+2) = leap == 0 ? 28 : 29;
#include<stdio.h>
#include<limits.h>
char month_tab[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char *day_tab = month_tab;
int input(int min, int max);
int day_of_year(int month, int m_day);
void month_day(int day, int *pmonth, int *pm_day);
int main(void)
{
int year, month, m_day, day;
int day_max;
int leap;
printf("Enter the year: ");
year = input(1, INT_MAX);
leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
*(day_tab+2) = leap == 0 ? 28 : 29;
printf("Enter the month: ");
month = input(1, 12);
printf("Enter the number of days in month %d: ",month);
day_max = *(day_tab + month);
m_day = input(1, day_max);
day = day_of_year(month, m_day);
printf("\nThe number of days of %d-%d-%d in %d is: "
"%d.\n",year,month,m_day,year,day);
printf("\nEnter the year: ");
year = input(1, INT_MAX);
leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
*(day_tab+2) = leap == 0 ? 28 : 29;
printf("Enter the day of year: ");
day = input(1, leap == 0 ? 365 : 366);
month_day(day, &month, &m_day);
printf("%d in %d is %d-%d-%d.\n",day, year, year, month, m_day);
return 0;
}
int input(int min, int max)
{
int status;
long int n;
int ok = 1;
while (ok) {
status = scanf("%ld", &n);
if (status != 1 && n < min && n > max && getchar() != '\n') {
printf("Enter again (%d ~ %d): ",min, max);
while (getchar() != '\n')
continue;
continue;
}
else
return (int)n;
}
}
int day_of_year(int month, int m_day)
{
int i;
int day = m_day;
day_tab++;
for (i = 1; i < month; i++)
day += *day_tab++;
return day;
}
void month_day(int day, int *pmonth, int *pm_day)
{
*pmonth = 1;
day_tab = month_tab + 1;
int m, d;
while (day / *day_tab) {
day -= *day_tab++;
(*pmonth)++;
}
*pm_day = day;
}
结果:
Enter the year: 2019
Enter the month: 10
Enter the number of days in month 10: 27
The number of days of 2019-10-27 in 2019 is: 300.
Enter the year: 2019
Enter the day of year: 300
300 in 2019 is 2019-10-27.
10. Write the program expr, which evaluates a reverse Polish expression from the command line, where each operator or operand is a separate argument. For example,
expr 2 3 4 + *
evaluates 2 * (3+4).
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#define NUMBER 'N'
#define FUNC 'F'
int getop(char *s);
void push(double f);
double pop(void);
void mathfunc(char *s);
int main(int argc, char *argv[])
{
if (argc == 1) {
printf("Usage: expression\n");
return 0;
}
int i, op;
double op2, sum;
for (i = 1; i < argc; i++) {
op = getop(argv[i]);
switch (op) {
case NUMBER:
push(atof(argv[i]));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("Zero divisor\n");
break;
case '%':
op2 = pop();
if (op2 != 0.0)
push(fmod(pop(),op2));
else
printf("Zero divisor\n");
break;
case FUNC:
mathfunc(argv[i]);
break;
default:
printf("Error: unknown command %s\n",argv[i]);
break;
}
}
sum = pop();
printf("%.8g\n",sum);
return 0;
}
//push() pop()
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
void push(double f)
{
if (sp < MAXVAL)
val[sp++] = f;
else
printf("Error: stack full, can't push %g\n",f);
}
double pop(void)
{
if (sp > 0)
return val[--sp];
else {
printf("Error: stack empty\n");
return 0.0;
}
}
//math function
void mathfunc(char *s)
{
double op2;
if (strcmp(s, "sin") == 0)
push(sin(pop()));
else if (strcmp(s, "cos") == 0)
push(cos(pop()));
else if (strcmp(s, "tan") == 0)
push(tan(pop()));
else if (strcmp(s, "pow") == 0) {
op2 = pop();
push(pow(pop(), op2));
}
else if (strcmp(s, "exp") == 0)
push(exp(pop()));
}
//getop()
int getop(char *s)
{
int i;
int isnum = 0;
if (!isdigit(s[0]) && s[0] != '.' && s[0] != '-') {
if (strcmp(s,"sin") == 0 || strcmp(s,"cos") == 0\
|| strcmp(s,"tan") == 0 || strcmp(s,"pow") == 0 \
|| strcmp(s, "exp") == 0)
return FUNC;
else if (strcmp(s, "+") == 0)
return '+';
else if (strcmp(s, "*") == 0)
return '*';
else if (strcmp(s, "/") == 0)
return '/';
else if (strcmp(s, "%") == 0)
return '%';
else
return -1;
}
if (strcmp(s, "-") == 0)
return '-';
i = 0;
if (s[0] == '-')
while (isdigit(s[++i]))
isnum = 1;
if (isdigit(s[i])) {
isnum = 1;
while (isdigit(s[++i]))
isnum = 1;
}
if (s[i] == '.')
while (isdigit(s[++i]))
isnum = 1;
if (isnum == 0 || s[i] != '\0')
return -1;
else
return NUMBER;
}
输出:
./expr 2 3 - sin 2 +
1.158529
11. Modify the program entab and detab (written as exercises in Chapter 1) to accept a list of tab stops as arguments. Use the default tab settings if there are no arguments.
detab
对于 detab 函数,将输入中的制表符换成自定义的n个空格。
分析:
1. tab 规则:
对于 tab 的规则,本系统上是8个空格,tab 有对齐功能,如:
//tab 测试程序
#include<stdio.h>
#include<string.h>
int main(void)
{
printf("\npress [tab]:\na dj djs sdjl jdl djs\n");
printf("a jd sjd ksdj kskdl \n");
printf("\ninput \t:\n");
printf("a\tab\tabc\tabcd\tabcdefg\tabcdefgh\tabcd\n");
return 0;
}
输出:
press [tab]:
a dj djs sdjl jdl djs
a jd sjd ksdj kskdl
input :
a ab abc abcd abcdefg abcdefgh abcd
根据结果可知,tab 结束位置为8的倍数。
2. 实现功能:
题目需要实现的 detab 的功能是通过命令行输入tab的参数,可以输入多个,如无参数,则按照默认的是8的倍数,如输入参数8,17,则tab停止的位置分别8和17。实现功能有两种:
a. 当输入一行字符串按 [enter] 换行后,输出将 tab 替换后的字符串。
b. 输入多行字符串(以空行结束输入),完成后将全部字符串替换输出。
3. 思路:
-
约定: 当遇见一个制表符 \t 时,该位置为 tab start,从该位置开始填充空格,最后一个空格位置为 tab stop。
认为第一个位置下标为1,则如果 tab 值为8,tab stop 的位置为8的倍数位置。
即遇到一个制表符,从该位置填充空格直到下一个 tab stop的位置。如:tab stop为3,则空格填充位置为 3 ~ 8,第9个位置无空格。如果tab stop 为7,则空格填充 7 ~ 8两个空格。
-
如果输入一行立马反馈将该行替换后输出,可以用 getchar() 获取单个字符,用一个变量记录位置(个数),遇到制表符时,查看当前位置以及下一个 tab stop 位置填充空格。
如果通过命令行输入 tab stop 位置,则需要一个数组存放位置状态,这样当输入第 n 个字符为制表符时,查看数组对应的第 n 个元素状态,如果为 tab stop 位(如设置1为tab stop,0为其他),则putchar输出空格,结束后继续扫描后面字符。
需要解决是存放tab 状态的数组尺寸设置多大,可以自定义一个尺寸如 len,如果输入长度超过 len,则换行,下标重置,这样如果输入tab stop 参数大于 len 就没用到。
另一种可以先找出输入参数最大的位置数,以该位置长度为一行最大的长度。
-
如果输入多行结束后一次输出,则需要将输入的字符串存起来,可以用指针数组存储,数组每个元素对应输入的每行字符串的首字符地址。
假设输入参数最后一个 tab stop 位为25,则每行尺寸为26(25+1)(注意先检查参数是否合法,非数字错误提示并推出)
指针数组的大小:数组元素的个数为输入的行数,自定义一个值 LINE,则输入行数达到个数或者遇到空行时结束输入。
一个 tab 状态一维数组,尺寸为 25,设置状态位,1为stop,0为其他。
每一行的处理:
(1)如果设定的一行最大值为len,则如果输入的一行超过len,则分成多行处理,赋值给多个指针。
(2)同上,如果一行超过分成多行输出,但仍然算作一行赋值给一个指针,即扫描到一个换行符才算一行,nlines加1。第一种好处理,扫描达到数目后,如没有换行符,添加空结束符,如有换行符,换行符替换为空结束符。但这样可能会导致多余的输入的字符未输出。
第二种需要改 getline 函数,如果数目达到但未扫描到换行符,在末尾加换行符,然后另分配空间继续扫描,直到扫到换行符。将几个数组合并(按顺序在数组末尾添加空结束符变成字符串,再用 strcat 函数,后面的字符串会覆盖之前的空结束符)为一个字符串赋给指针,这样nlines加1。程序实现第一种,第二种以后补充。
4. 程序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define MAXLINES 100
int checkargs(int argc, char *argv[]);
int args_max(int argc, char *argv[]);
void settab(char *tab, int maxlen, int argc, char *argv[]);
int mgetline(char *s, char *tab, int maxlen);
void writelines(char *lineptr[], int nlines);
int main(int argc, char *argv[])
{
char *lineptr[MAXLINES];
int maxlen;//maxlen为一行字符数,不包括换行符,空结束符,实际存储字符串要加1
//判断命令行参数
if (argc == 1)
maxlen = 40;
else {
int ok;
ok = checkargs(argc, argv);
if (ok == 0) {
fprintf(stdout, "Error: nonumeric arguments\n");
exit(EXIT_FAILURE);
}
else
maxlen = args_max(argc, argv);
}
//设置 tab stop
char tab[maxlen];
settab(tab, maxlen, argc, argv);
//读字符串
int nlines = 0;
char line[maxlen+1];
int len;
printf("Input stings (maximum lines and character size : %d,"
"%d):\n",MAXLINES, maxlen);
while (nlines < MAXLINES && (len = mgetline(line, tab, maxlen)) > 0) {
lineptr[nlines] = malloc((len+1) * sizeof(char));
strcpy(lineptr[nlines++], line);
}
printf("Strings with detab:\n");
writelines(lineptr, nlines);
//释放内存
for (int i = 0; i < nlines; i++)
free(lineptr[i]);
return 0;
}
int checkargs(int argc, char *argv[])
{
int i, j;
int ok = 1;
for (i = 1; i < argc; i++) {
for(j = 0; *(argv[i]+j) != '\0'; j++)
if (!isdigit(*(argv[i]+j)))
return 0;
}
return 1;
}
int args_max(int argc, char *argv[])
{
int i;
int max;
for (max = atoi(argv[1]),i = 2; i < argc; i++)
if (atoi(argv[i]) > max)
max = atoi(argv[i]);
return max;
}
void settab(char *tab, int maxlen, int argc, char *argv[])
{
int i, pos;
if (argc == 1)
for (i = 0; i < maxlen; i++)
tab[i] = (i+1) % 8 == 0 ? 1 : 0;
else {
for (i = 0; i < maxlen; i++)
tab[i] = 0;
for (i = 1; i < argc; i++) {
pos = atoi(argv[i]);
tab[pos-1] = 1;
}
}
}
//mgetline 存储一行数据
int mgetline(char *s, char *tab, int n)
{
int i, c;
for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++) {
if (c == '\t') {
while (tab[i] != 1 && i+1 < n)
s[i++] = ' ';
s[i] = ' ';
}
else
s[i] = c;
}
s[i] = '\0';
return i;
}
//writelines
void writelines(char *lineptr[], int nlines)
{
int i;
for (i = 0; i < nlines; i++)
printf("%s\n",lineptr[i]);
}
输出:
./detab.o 3 6 9 12 15 18
Input stings (maximum lines and character size : 100,18):
a bc def ghi l
ab cde frh jklm no
pqu vwxy z1234 56789 111
23456 32123 321234 323233 ewkl
Strings with detab:
a bc def ghi
l
ab cde frh jkl
m no
pqu vwxy z1234
56789 111
23456 32123 321234
323233 ewkl
entab
将输入的字符串中的空格替换成最少数目的制表符和空格,最终达到的效果一样,这样减少了字符的数目。
tab stop 的设置和 detab 相同。
这个程序和 detab 程序不同在于 mgetline 函数。当识别到空格时,继续向后扫描,如果后面空格中有 tab stop 位,则第一个空格位置用制表符替换,重复继续,直到空格中无 tab stop位,则仍用空格。
注意:如果扫到一个空格,该字符是该行容纳的最后一个字符,那么如果继续向后扫描,扫描到不能替换的空格以及非空格字符,不能再存在该行,这时应该将多余的字符返回到缓冲区中,可以用 ungetch 函数。而 getch 和 ungetch 的缓冲区大小为 maxlen ,该值要计算,因此定义为外部变量使用。
注意: 如果第一个空格处于 tab stop 位,不替换。
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#define MAXLINES 100
int maxlen; //创建为外部变量
int checkargs(int argc, char *argv[]);
int max_args(int argc, char *argv[]);
void settab(int *tab, int argc, char *argv[]);
int mgetline(char *s, int *tab, int n);
void writelines(char *lineptr[], int nlines);
int main(int argc, char *argv[])
{
//检查命令行参数
if (argc == 1)
maxlen = 40;
else {
int ok;//检查参数是否合法,非数字错误,退出
if ((ok = checkargs(argc, argv)) == 0) {
fprintf(stdout, "Error: nonumeric argument(s)\n");
exit(EXIT_FAILURE);
}
else {
int max = max_args(argc, argv);
maxlen = max;
}
}
//设置 tab stop
int tab[maxlen];
settab(tab, argc, argv);
//读取字符串输入
printf("Enter strings (maximum lines: %d, character size: "
"%d):\n",MAXLINES, maxlen);
char *lineptr[MAXLINES];
int nlines = 0; //实际扫描的行数
int len; //实际每行扫描的字符数
char line[maxlen+1]; //临时存放每行字符串
//当扫描行数未超过且不是空行时循环扫描
while (nlines < MAXLINES && (len = mgetline(line, tab, maxlen)) > 0) {
lineptr[nlines] = (char *)malloc((len + 1) * sizeof(char));
strcpy(lineptr[nlines++], line);
}
printf("Strings with entab:\n");
writelines(lineptr, nlines);
//释放内存
for (int i = 0; i < nlines; i++)
free(lineptr[i]);
return 0;
}
int checkargs(int argc, char *argv[])
{
int i, j;
for (i = 1; i < argc; i++)
for (j = 0; *(argv[i]+j) != '\0'; j++)
if (!isdigit(*(argv[i]+j)))
return 0;
return 1;
}
int max_args(int argc, char *argv[])
{
int i;
int max = atoi(argv[1]);
for (i = 2; i < argc; i++)
if (atoi(argv[i]) > max)
max = atoi(argv[i]);
return max;
}
void settab(int *tab, int argc, char *argv[])
{
int i;
if (argc == 1)
for (i = 0; i < maxlen; i++)
tab[i] = (i+1) % 8 == 0 ? 1 : 0;
else {
for (i = 0; i < maxlen; i++)
tab[i] = 0;
for (i = 1; i < argc; i++)
tab[atoi(argv[i]) -1] = 1;
}
}
//getch ungetch
#define BUFSIZE 100
int bufp = 0;
char buf[BUFSIZE];
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
//mgetline 获取字符串并处理
int mgetline(char *s, int *tab, int n)
{
int i, j, k;//i 为原来下标,j为替换后下标,k临时计算空格
int c, ct;
int t_count = 0;
int stop[maxlen];//存放 tab stop下标
int x = 0; //stop 下标,值为 tab stop个数
for (i = 0, j = 0; i < n && (c = getch()) != EOF && c != '\n'; i++) {
if (c == ' ') {
for (k = i+1; k < n && (c = getch()) == ' '; k++)
if (tab[k] == 1) {
stop[x++] = k;
t_count++;
}
//注意i处如果为 tab stop 位,则当空格
//ct 为除去制表符后空格的个数
if (tab[i] == 1) {
s[j++] = ' ';
ct = t_count > 0 ? k-1-stop[x-1] : k-1-i;
}
else if (tab[i] == 0)
ct = t_count > 0 ? k-1-stop[x-1] : k-i;
for (; t_count > 0; t_count--)
s[j++] = '\t';
for (; ct > 0; ct--)
s[j++] = ' ';
if (k == n) {
s[j] = '\0';
ungetch(c);
return j;
}
else if (k < n) {
s[j++] = c;
i = k;
}
}
else
s[j++] = c;
}
s[j] = '\0';
return j;
}
void writelines(char *lineptr[], int nlines)
{
int i;
for (i = 0; i < nlines; i++)
puts(lineptr[i]);
}
输出:
注意:
程序如果通过命令行输入tab stop 的参数,要与默认的一样是8的倍数才能与输出一样,否则需要再将有制表符的字符串通过detab输出。
遇到第一个空格时,要判断该空格位置是否是 tab stop 位,如果是,先将空格赋值,如果不是,后面有制表符,该位置才是制表符。对应程序:
//ct 为除去制表符后空格的个数
if (tab[i] == 1) {
s[j++] = ' ';
ct = t_count > 0 ? k-1-stop[x-1] : k-1-i;
}
else if (tab[i] == 0)
ct = t_count > 0 ? k-1-stop[x-1] : k-i;
12. Extend entab and detab to accept the shorthand
entab -m +n
to mean tab stops every n columns, starting at column m. Choose convenient (for the user) default behavior.
需要实现功能,接受特定格式命令行参数,-m 指从m行开始,+n 指每增加 n 行为一个 tab stop 位。
思路:
检查命令行参数,如果没有设置参数,采用默认形式,每8列为tab stop 位,下标从 0 开始,因此,m 为7,n 为8。
如果有参数,检查是否合法,形式只能是 -m, +n,如果参数不合法, 提示非法参数且采用默认参数。
除了检查参数的函数 checkarg 和设置tab stop 的函数 settab 需要修改外,其他不变。
将detab 和 entab 放在一个程序,先输出原本字符串,再经过 entab ,最后 detab 变回原来字符串。
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#define MAXLINES 100
#define MAXLEN 100
int checkargs(int argc, char *argv[]);
void settab(int *tab, int ok, int argc, char *argv[]);
int mgetline(char *s, int n);
void entab(char *s, int *tab);
void detab(char *s, char *a, int *tab);
void writelines(char *lineptr[], int nlines);
int main(int argc, char *argv[])
{
int ok = checkargs(argc, argv);
if (ok == 0)
printf("No valid tab stop arguments. Default value (-7 +8) is used.\n");
int tab[MAXLEN];
settab(tab, ok, argc, argv);
printf("Enter strings (maximum lines: %d, maximum characters: "
"%d):\n",MAXLINES, MAXLEN);
//获取输入字符串
int len;
char line[MAXLEN+1];
int nlines = 0;
char *lineptr[MAXLINES];
while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
strcpy(lineptr[nlines++], line);
}
//输出原始字符串
printf("\nOriginal strings:\n");
writelines(lineptr, nlines);
//用 entab 函数处理字符串空格
printf("\nStrings processed by entab:\n");
for (int i = 0; i < nlines; i++) {
entab(lineptr[i], tab);
puts(lineptr[i]);
}
//用detab还原字符串
printf("\nStrings processed by detab:\n");
char a[MAXLEN+1];
for (int i = 0; i < nlines; i++) {
detab(lineptr[i], a, tab);
puts(a);
}
//释放内存
for (int i = 0; i < nlines; i++)
free(lineptr[i]);
return 0;
}
int checkargs(int argc, char *argv[])
{
int i, j;
int ok;
if (argc == 3) {
if (*argv[1] == '-' && *argv[2] == '+') {
for (i = 1; i < argc; i++)
for (j = 1; argv[i][j] != '\0'; j++)
if (!isdigit(argv[i][j]))
return 0;
return 1;
}
}
else
return 0;
}
void settab(int *tab, int ok, int argc, char *argv[]) {
int i, j;
int m, n;
if (ok == 0) {
m = 7;
n = 8;
}
else {
m = atoi(++argv[1]);
n = atoi(++argv[2]);
}
for (i = 0; i < MAXLEN; i++)
tab[i] = 0;
for (i = m; i < MAXLEN; i += n)
tab[i] = 1;
}
int mgetline(char *s, int n)
{
int i, c;
for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
s[i] = c;
s[i] = '\0';
return i;
}
void entab(char *s, int *tab)
{
int i, j, k;
int len = strlen(s);
int stop[len];
int x = 0;
int t_count = 0;
int space;
for (i = 0, k = 0; i < len; i++) {
if (s[i] == ' ') {
//注意,如果该处空格位置为tab stop位,当作空格
if (tab[i] == 1)
s[k++] = ' ';
for (j = i+1; j < len && s[j] == ' '; j++)
if (tab[j] == 1) {
stop[x++] = j;
t_count++;
}
if (j == i+1) //第一个空格后面没有空格
continue;
else {
// i+1 ~ j-1 之间都为空格
space = (t_count > 0) ? j - 1 - stop[x-1] : j - i - 1;
for (; t_count > 0; t_count--)
s[k++] = '\t';
for (; space > 0; space--)
s[k++] = ' ';
if (j == len)
break;
else {
s[k++] = s[j];
i = j;
}
}
}
else
s[k++] = s[i];
}
s[k] = '\0';
}
void detab(char *s, char *a, int *tab)
{
int i, j, k;
int len = strlen(s);
for (i = 0, k = 0; i < len; i++) {
if (s[i] == '\t') {
for (j = k; j < MAXLEN && tab[k] == 0; j++)
a[k++] = ' ';
if (j == MAXLEN)
break;
else
a[k++] = ' ';
}
else
a[k++] = s[i];
}
a[k] = '\0';
}
void writelines(char *lineptr[], int nlines)
{
int i;
for (i = 0; i < nlines; i++)
puts(lineptr[i]);
}
输出:
如果用默认参数,则entab后输出与原来间隔一致,否则,自己设置参数,entab 后间隙变化(因为与系统制表符个数不统一),但 detab 后还是会变成原样。
13. Write the program tail, which prints the last n lines of its input. By default, n is set to 10, let us say, but it can be changed by an optional argument so that tail -n prints the last n lines.
The program should behave rationally no matter how unreasonable the input or the value of n. Write the program so it makes the best use of available storage; lines should be stored as in the sorting program of Section 5.6, not in a two-dimensional array of fixed size.
分析:
程序需要实现功能为:打印输入的最后n行,如果没有设置参数n,默认为10。
思路:
- 通过命令行参数设置 n。检查参数合法性,非法采用默认值。
- 设定接受的最大行数,每行的最大字符数,如果输入一行超过限制字符,是将后面的去掉不要还是换成多行。
如果多余字符不去掉,多余字符会多占用行,这样输出的行并非真是输入的行。
如果将多余的字符去掉,也会丢失内容。
这里用第一种,将多余字符换行。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int checkargs(int argc, char *argv[]);
int mgetline(char *s, int n);
int main(int argc, char *argv[])
{
int tail;
int ok = checkargs(argc, argv);
if (ok == 0) {
printf("No valid argument. Default 10 is taken.\n");
tail = 10;
}
else
tail = atoi(++argv[1]);
//获取输入字符串
printf("Enter strings (maximum rows: %d, maximum columns"
" : %d):\n",MAXLINES, MAXLEN);
char *lineptr[MAXLINES];
char line[MAXLEN+1];
int nlines = 0;
int len;
while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
strcpy(lineptr[nlines++], line);
}
printf("\nThe last %d lines:\n",tail);
int i = (nlines-1) - (tail-1) > 0 ? nlines - tail : 0;
for (; i < nlines; i++)
puts(lineptr[i]);
return 0;
}
int checkargs(int argc, char *argv[])
{
int ok;
if (argc == 2) {
if (*argv[1] == '-') {
for (int i = 1; argv[1][i] != '\0'; i++)
if (!isdigit(argv[1][i]))
return 0;
return 1;
}
}
return 0;
}
int mgetline(char *s, int n)
{
int i, c;
for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
s[i] = c;
s[i] = '\0';
return i;
}
输出:
./tail -5
Enter strings (maximum rows: 100, maximum columns : 100):
sdj asd
sjde
djs
dsjde
djsj
wjdjf
dsdjjf
sdnd
djj
sjej
The last 5 lines:
wjdjf
dsdjjf
sdnd
djj
sjej
14. Modify the sort program to handle a -r flag, which indicates sorting in reverse (decreasing) order. Be sure that -r works with -n.
**约定:**命令行参数有 -r 和 -n 时,以逆序,比较数字,否则默认为正序,比较字母。如果出现非法字符,提示非法字符。
增加一个逆序参数两种方法:
- 仍然用之前的正序排序,如果参数有 -r,则反向输出。
- 在比较函数中加入是否逆序的参数,可以逆序排序,以下程序使用该方法。与之前不同之处在增加参数 reverse,该参数在 比较程序中起作用,如果为1,则之前返回正值的改为范围负值,因此排序程序不用改动。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int mgetline(char *s, int n);
int numcmp(char *s1, char *s2, int r);
int mstrcmp(char *s1, char *s2, int r);
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int), int
r);
void writelines(char *v[], int nlines);
int main(int argc, char *argv[])
{
//检查参数
int c, numeric = 0, reverse = 0;
while (--argc > 0 && (*++argv)[0] == '-')
if (strcmp(*argv, "-n") == 0)
numeric = 1;
else if (strcmp(*argv, "-r") == 0)
reverse = 1;
else {
printf("find: illegal option %s\n",*argv);
argc = 0;
}
//输入字符串
printf("Please enter strings (maximum rows: %d, maximum columns: "
"%d\n",MAXLINES, MAXLEN);
int nlines = 0;
char *lineptr[MAXLINES];
char line[MAXLEN+1];
int len;
while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
strcpy(lineptr[nlines++], line);
}
//输出原始字符串
printf("\nOriginal strings:\n");
writelines(lineptr, nlines);
//排序
mqsort((void **)lineptr, 0, nlines-1,(int (*)(void*, void*, int))(numeric ?
numcmp : mstrcmp), reverse);
printf("\nSorted strings in %s order:\n",reverse > 0 ? "decreasing" : "increasing");
writelines(lineptr, nlines);
//释放内存
for (int i = 0; i < nlines; i++)
free(lineptr[i]);
return 0;
}
int mgetline(char *s, int n)
{
int i, c;
for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
s[i] = c;
if (c != '\n' && c != EOF)
while (getchar() != '\n')
continue;
s[i] = '\0';
return i;
}
//快速排序
#define cutoff 3
void swap(void *v[], int s1, int s2)
{
void *tmp;
tmp = v[s1];
v[s1] = v[s2];
v[s2] = tmp;
}
void InsertionSort(void *v[], int n, int (*comp)(void *, void *, int), int r)
{
int i, p;
char tmp[MAXLEN+1];
for (p = 1; p < n; p++) {
strcpy(tmp, v[p]);
for (i = p; i > 0 && (*comp)(v[i-1], tmp, r) > 0; i--)
strcpy(v[i], v[i-1]);
strcpy(v[i], tmp);
}
}
int numcmp(char *s1, char *s2, int reverse)
{
double v1, v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1 < v2)
return reverse == 0 ? -1 : 1;
else if (v1 > v2)
return reverse == 0 ? 1 : -1;
else
return 0;
}
int mstrcmp(char *s1, char *s2, int reverse)
{
int i;
for (i = 0; s1[i] == s2[i]; i++)
if (s1[i] == '\0')
return 0;
return reverse == 0 ? s1[i] - s2[i] : s2[i] - s1[i];
}
void *Median3(void *v[], int left, int right, int (*comp)(void*, void*, int),int r)
{
int center = (left + right) / 2;
if ((*comp)(v[left], v[center], r) > 0)
swap(v, left, center);
if ((*comp)(v[left], v[right], r) > 0)
swap(v, left, right);
if ((*comp)(v[center], v[right], r) > 0)
swap(v, center, right);
swap(v, center, right-1);
return v[right-1];
}
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int), int
r)
{
int i, j;
void *pivot;
if (left + cutoff <= right) {
pivot = Median3(v, left, right, comp, r);
i = left;
j = right-1;
for (; ; ) {
while ((*comp)(v[++i], pivot, r) < 0)
;
while ((*comp)(v[--j], pivot, r) > 0)
;
if (i < j)
swap(v, i, j);
else
break;
}
swap(v, i, right-1);
mqsort(v, left, i-1, comp, r);
mqsort(v, i+1, right, comp, r);
}
else
InsertionSort(v+left, right - left + 1, comp, r);
}
//writelines
void writelines(char *v[], int nlines)
{
for (int i = 0; i < nlines; i++)
puts(v[i]);
}
输出:
./qsort1.o -r -n
Please enter strings (maximum rows: 100, maximum columns: 100
2
4
0
1
3
Original strings:
2
4
0
1
3
Sorted strings in decreasing order:
4
3
2
1
0
15. Add the option -f to fold upper and lower case together, so that case distinctions are not made during sorting; for example, a and A compare equal.
分析:程序增加一个参数 -f,不区分大小写
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int mgetline(char *s, int n);
void writelines(char *v[], int n);
int numcmp(char *s1, char *s2, int r, int f);
int mstrcmp(char *s1, char *s2, int r, int f);
void mqsort(void *v[], int left, int right, int (*comp)(void*,void*,int,int),\
int r, int f);
int main(int argc, char *argv[])
{
//检查参数
int numeric = 0, reverse = 0, fold = 0;
while (--argc > 0 && (*++argv)[0] == '-')
if (strcmp(*argv, "-r") == 0)
reverse = 1;
else if (strcmp(*argv, "-n") == 0)
numeric = 1;
else if (strcmp(*argv, "-f") == 0)
fold = 1;
else {
printf("find : illegal option %s\n",*argv);
argc = 0;
}
//获取字符串
printf("Enter strings (maximum rows: %d, maximum columns: "
"%d\n",MAXLINES,MAXLEN);
int nlines = 0;
int len;
char *lineptr[MAXLINES];
char line[MAXLEN+1];
while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
strcpy(lineptr[nlines++], line);
}
printf("\nOriginal strings:\n");
writelines(lineptr, nlines);
//排序
mqsort((void **)lineptr, 0, nlines-1, (int(*)(void*, void*, int,\
int))(numeric ? numcmp : mstrcmp), reverse, fold);
printf("\nSorted string in %s order and %s upper and lower case "
"together.\n",reverse ? "decreasing" : "increasing",fold ? "fold":"unfold");
writelines(lineptr, nlines);
//释放内存
for (int i = 0; i < nlines; i++)
free(lineptr[i]);
return 0;
}
//mgetline
int mgetline(char *s, int n)
{
int i, c;
for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
s[i] = c;
s[i] = '\0';
if (c != '\n' && c != EOF)
while (getchar() != '\n')
continue;
return i;
}
//快速排序
#define cutoff 3
void swap(void *v[], int i, int j)
{
void *tmp;
tmp = v[i];
v[i] = v[j];
v[j] = tmp;
}
void *Median3(void *v[], int left, int right, int (*comp)(void*, void*, int,
int), int r, int f)
{
int center = (left + right) / 2;
if ((*comp)(v[left], v[center], r, f) > 0)
swap(v, left, center);
if ((*comp)(v[left], v[right], r, f) > 0)
swap(v, left, right);
if ((*comp)(v[center], v[right], r, f) > 0)
swap(v, center, right);
swap(v, center, right-1);
return v[right-1];
}
int numcmp(char *s1, char *s2, int r, int f)
{
double v1, v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1 < v2)
return r ? 1 : -1;
else if (v1 > v2)
return r ? -1 : 1;
else
return 0;
}
int mstrcmp(char *s1, char *s2, int r, int f)
{
int i;
for ( i = 0; s1[i] == s2[i] || (f && tolower(s1[i]) == tolower(s2[i])); i++)
if (s1[i] == '\0')
return 0;
if (f)
return r ? tolower(s2[i]) - tolower(s1[i]) : tolower(s1[i]) -
tolower(s2[i]);
else
return r ? s2[i]-s1[i] : s1[i]-s2[i];
}
void InsertionSort(void *v[], int n, int (*comp)(void*,void*,int,int),int r, int
f)
{
int i, p;
char tmp[MAXLEN+1];
for (p = 1; p < n; p++) {
strcpy(tmp,v[p]);
for (i = p; i > 0 && (*comp)(v[i-1],tmp,r,f) > 0; i--)
strcpy(v[i], v[i-1]);
strcpy(v[i], tmp);
}
}
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int,
int),int r, int f)
{
void *pivot;
int i, j;
if (left + cutoff <= right) {
pivot = Median3(v, left, right, comp, r, f);
i = left;
j = right-1;
for (; ; ) {
while ((*comp)(v[++i],pivot,r,f) < 0)
;
while ((*comp)(v[--j],pivot,r,f) > 0)
;
if (i < j)
swap(v,i,j);
else
break;
}
swap(v, i, right-1);
mqsort(v, left, i-1, comp, r, f);
mqsort(v, i+1, right, comp, r, f);
}
else
InsertionSort(v+left, right-left+1, comp, r, f);
}
//writelines
void writelines(char *v[], int nlines)
{
for (int i = 0; i < nlines; i++)
puts(v[i]);
}
输出:
./qsort2.o -f
Enter strings (maximum rows: 100, maximum columns: 100
a
B
D
c
C
Original strings:
a
B
D
c
C
Sorted string in increasing order and fold upper and lower case together.
a
B
C
c
D
从结果可知:测试中 c 在 C前面,但输出时两者交换了,相等的地方改变了次序。
16. Add the -d (``directory order’’) option, which makes comparisons only on letters, numbers and blanks. Make sure it works in conjunction with -f.
分析:
程序在之前基础上加上参数 -d,只比较字母,数字和空格,遇到其他字符跳过不比较。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int mgetline(char *s, int n);
void writelines(char *v[], int n);
int numcmp(char *s1 , char *s2, int r, int f, int d);
int mstrcmp(char *s1, char *s2, int r, int f, int d);
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int, int,
int), int r, int f, int d);
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int,
int),int r, int f, int d);
int main(int argc, char *argv[])
{
//检查参数
int numeric = 0, reverse = 0, fold = 0, directory = 0;
while (--argc > 0 && (*++argv)[0] == '-')
if (strcmp(*argv, "-n") == 0)
numeric =1;
else if (strcmp(*argv, "-r") == 0)
reverse = 1;
else if (strcmp(*argv, "-f") == 0)
fold = 1;
else if (strcmp(*argv, "-d") == 0)
directory = 1;
else {
printf("find: illeagal argument %s\n", *argv);
argc = 0;
}
//输入字符串
printf("Enter strings (maximum rows: %d, maximum columns: %d\n",MAXLINES,
MAXLEN);
int nlines = 0;
int len;
char *lineptr[MAXLINES];
char line[MAXLEN+1];
while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
strcpy(lineptr[nlines++], line);
}
printf("\nOriginal strings:\n");
writelines(lineptr, nlines);
//排序
mqsort((void **)lineptr, 0, nlines-1, (int (*)(void*, void*, int, int,
int))(numeric ? numcmp : mstrcmp), reverse, fold, directory);
printf("\nSorted strings:\n");
writelines(lineptr, nlines);
//释放内存
for (int i = 0; i < nlines; i++)
free(lineptr[i]);
return 0;
}
int mgetline(char *s, int n)
{
int i, c;
for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
s[i] = c;
s[i] = '\0';
if (c != '\n' && c != EOF)
while (getchar() != '\n')
continue;
return i;
}
void writelines(char *v[], int n)
{
int i;
for (i = 0; i < n; i++)
puts(v[i]);
}
//快速排序
#define cutoff 3
void swap(void *v[], int i, int j)
{
void *temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}
void *Median3(void *v[], int left, int right, int (*comp)(void*, void*, int,
int,int), int r, int f, int d)
{
int center = (left + right) / 2;
if ((*comp)(v[left], v[center], r, f, d) > 0)
swap(v, left, center);
if ((*comp)(v[left], v[right], r, f, d) > 0)
swap(v, left, right);
if ((*comp)(v[center], v[right], r, f, d) > 0)
swap(v, center, right);
swap(v, center, right-1);
return v[right-1];
}
int numcmp(char *s1, char *s2, int r, int f, int d)
{
double v1, v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1 > v2)
return r ? -1 : 1;
else if (v1 < v2)
return r ? 1 : -1;
else
return 0;
}
int mstrcmp(char *s1, char *s2, int r, int f, int d)
{
int i, j;
for (i = 0, j = 0; s1[i] != '\0'&& s2[j] != '\0'; ) {
if (d) {
if (!isalnum(s1[i]) && !isspace(s1[i])) {
i++;
continue;
}
if (!isalnum(s2[j]) && !isspace(s2[j])) {
j++;
continue;
}
}
if (f) {
if (tolower(s1[i]) != tolower(s2[j]))
return r ? tolower(s2[j]) - tolower(s1[i]) : tolower(s1[i]) -
tolower(s2[j]);
else
i++;
j++;
}
else {
if (s1[i] != s2[j])
return r ? s2[j] - s1[i] : s1[i] - s2[j];
else
i++;
j++;
}
}
if (s1[i] == s2[j] && s1[i] == '\0')
return 0;
else if (s1[i] == '\0')
return r ? 1 : -1;
else if (s2[j] == '\0')
return r ? -1 : 1;
}
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int,
int),int r, int f, int d)
{
int i, p;
void *tmp;
for (p = 1; p < n; p++) {
tmp = v[p];
for (i = p; i > 0 && (*comp)(v[i-1], tmp, r, f, d) > 0; i--)
v[i] = v[i-1];
v[i] = tmp;
}
}
void mqsort(void *v[], int left, int right, int (*comp)(void*, void *, int, int,
int), int r, int f, int d)
{
void *pivot;
int i, j;
if (left + cutoff <= right) {
pivot = Median3(v, left, right, comp, r, f, d);
i = left;
j = right-1;
for (; ;) {
while ((*comp)(v[++i],pivot,r,f,d) < 0)
;
while ((*comp)(v[--j],pivot,r,f,d) > 0)
;
if (i < j)
swap(v, i, j);
else
break;
}
swap(v, i, right-1);
mqsort(v, left, i-1, comp, r ,f, d);
mqsort(v, i+1, right, comp, r, f, d);
}
else
InsertionSort(v+left, right-left+1, comp, r, f, d);
}
本程序与前面更改地方:
之前:
void InsertionSort(void *v[], int n, int (*comp)(void *, void *, int), int r)
{
int i, p;
char tmp[MAXLEN+1];
for (p = 1; p < n; p++) {
strcpy(tmp, v[p]);
for (i = p; i > 0 && (*comp)(v[i-1], tmp, r) > 0; i--)
strcpy(v[i], v[i-1]);
strcpy(v[i], tmp);
}
}
现在:
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int,
int),int r, int f, int d)
{
int i, p;
void *tmp;
for (p = 1; p < n; p++) {
tmp = v[p];
for (i = p; i > 0 && (*comp)(v[i-1], tmp, r, f, d) > 0; i--)
v[i] = v[i-1];
v[i] = tmp;
}
}
除了添加参数 -d,本程序将指针直接赋值而不是之前赋值字符串。两种都能实现。但要么全部用复制字符串修改地址内容的方式,要么全部用修改地址的方式。
swap 函数也一样,两种方式都能实现。
输出测试:
./qsort3.o -r -f -d
Enter strings (maximum rows: 100, maximum columns: 100
8as
*a1 v
a1B2
1a(sd
-1Aa
Original strings:
8as
*a1 v
a1B2
1a(sd
-1Aa
Sorted strings:
a1B2
*a1 v
1a(sd
-1Aa
8as
只比较字母,数字和空格,且不区分大小写,逆序排布。空格的ascall值最小,其次数字,最后字母。
17. Add a field-searching capability, so sorting may bee done on fields within lines, each field sorted according to an independent set of options. (The index for this book was sorted with -df for the index category and -n for the page numbers.)
分析:
增加一个区域处理的功能,在范围内选择一段区域选择来处理字符串。
两种实现方式:这样不用命令行参数,因为要根据输入的行数选择。通过菜单选择实现更简单。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int input(int min, int max);
int mgetline(char *s, int n);
void writelines(char *v[], int nlines);
char getlet(char *s);
int numcmp(char *s1, char *s2, int r, int f, int d);
int mstrcmp(char *s1, char *s2, int r, int f, int d);
void mqsort(void *v[], int, int, int (*comp)(void*, void*, int, int, int), int
r, int f, int d);
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int, int),
int r, int f, int d);
int main(void)
{
//输入字符串
printf("Enter strings (maximum rows: %d, maximum columns: %d):\n",MAXLINES,
MAXLEN);
int nlines = 0;
char *lineptr[MAXLINES];
char line[MAXLEN+1];
int len;
while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
strcpy(lineptr[nlines++], line);
}
printf("\nOriginal strings:\n");
writelines(lineptr, nlines);
//选择参数
int left, right;
int ok = 1;
char c;
int numeric, reverse, fold, directory;
while (ok) {
printf("Please select a field (%d ~ %d):\n", 0, nlines-1);
left = input(0, nlines-1);
right = input(left, nlines-1);
printf("\nComparing strings by numeric value (n) or lexicographically (s): ");
c = getlet("ns");
numeric = c == 'n' ? 1 : 0;
printf("Sorting strings in decrasing order (d) or increasing order (i): ");
c = getlet("di");
reverse = c == 'd' ? 1 : 0;
if (numeric == 0) {
printf("Fold upper and lower case togehter ? <y/n> ");
c = getlet("yn");
fold = c == 'y' ? 1 : 0;
printf("Using directory order ? <y/n> ");
c = getlet("yn");
directory = c == 'y' ? 1 : 0;
}
//排序
mqsort((void **)lineptr, left, right, (int (*)(void*, void*, int, int,
int))(numeric ? numcmp : mstrcmp), reverse, fold, directory);
printf("\nSorted strings (lines between %d ~ %d sorted):\n",left, right);
writelines(lineptr, nlines);
printf("Select another field to sort again? <y/n> ");
c = getlet("yn");
if (c == 'y')
continue;
else
break;
}
//释放内存
for (int i = 0; i < nlines; i++)
free(lineptr[i]);
return 0;
}
int input(int min, int max)
{
int n, status;
int ok = 1;
while (ok) {
status = scanf("%d", &n);
if (status != 1 || n < min || n > max || getchar() != '\n') {
printf("Enter again (%d ~ %d): ", min, max);
while (getchar() != '\n')
continue;
continue;
}
else
return n;
}
}
int mgetline(char *s, int n)
{
int i, c;
for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
s[i] = c;
s[i] = '\0';
if (c != EOF && c != '\n')
while (getchar () != '\n')
continue;
return i;
}
void writelines(char *v[], int nlines)
{
int i;
for (i = 0; i < nlines; i++)
puts(v[i]);
}
char getlet(char *s)
{
char c;
while (c = getchar()) {
if (strchr(s, c) && getchar() == '\n')
return c;
else {
printf("Enter again: ");
while (getchar() != '\n')
continue;
}
}
}
//快速排序
#define cutoff 3
void swap(void *v[], int i, int j)
{
void *tmp;
tmp = v[i];
v[i] = v[j];
v[j] = tmp;
}
void *Median3(void *v[], int left, int right, int (*comp)(void*, void*, int,
int, int), int r, int f, int d)
{
int center = (left + right) / 2;
if ((*comp)(v[left], v[center], r, f, d) > 0)
swap(v, left, center);
if ((*comp)(v[left], v[right], r, f, d) > 0)
swap(v, left, right);
if ((*comp)(v[center], v[right], r, f, d) > 0)
swap(v, center, right);
swap(v, center, right-1);
return v[right-1];
}
int numcmp(char *s1, char *s2, int r, int f, int d)
{
double v1, v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1 < v2)
return r ? 1 : -1;
else if (v1 > v2)
return r ? -1 : 1;
else
return 0;
}
int mstrcmp(char *s1, char *s2, int r, int f, int d)
{
int i, j;
for (i = 0, j = 0; s1[i] != '\0' && s2[j] != '\0'; ) {
if (d) {
if (!isalnum(s1[i]) && !isspace(s1[i])) {
i++;
continue;
}
if (!isalnum(s2[j]) && !isspace(s2[j])) {
j++;
continue;
}
}
if (f) {
if (tolower(s1[i]) == tolower(s2[j])) {
i++;
j++;
}
else {
return r ? tolower(s2[j]) - tolower(s1[i]) : tolower(s1[i]) -
tolower(s2[j]);
}
}
else {
if (s1[i] == s2[j]) {
i++;
j++;
}
else
return r ? s2[j] - s1[i] : s1[i] - s2[j];
}
}
if (s1[i] == s2[j] && s1[i] == '\0')
return 0;
else if (s1[i] == '\0' && s2[j] != '\0')
return r ? 1 : -1;
else
return r ? -1 : 1;
}
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int, int),
int r, int f, int d)
{
int i, p;
void *tmp;
for (p = 1; p < n; p++) {
tmp = v[p];
for (i = p; i > 0 && (*comp)(v[i-1], tmp, r, f, p) > 0; i--)
v[i] = v[i-1];
v[i] = tmp;
}
}
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int, int,
int), int r, int f, int d)
{
void *pivot;
int i, j;
if (left + cutoff <= right) {
pivot = Median3(v, left, right, comp, r, f, d);
i = left;
j = right-1;
for (; ; ) {
while ((*comp)(v[++i], pivot, r, f, d) < 0)
;
while ((*comp)(v[--j], pivot, r, f, d) > 0)
;
if (i < j)
swap(v, i, j);
else
break;
}
swap(v, i, right-1);
mqsort(v, left, i-1, comp, r, f, d);
mqsort(v, i+1, right, comp, r, f, d);
}
else
InsertionSort(v+left, right-left+1, comp, r, f, d);
}
输出:
./qsort4.o
Enter strings (maximum rows: 100, maximum columns: 100):
ajde
8as
-1*aAb
ab-sda
AB1-s
Original strings:
ajde
8as
-1*aAb
ab-sda
AB1-s
Please select a field (0 ~ 4):
2
4
Comparing strings by numeric value (n) or lexicographically (s): s
Sorting strings in decrasing order (d) or increasing order (i): d
Fold upper and lower case togehter ? <y/n> y
Using directory order ? <y/n> y
Sorted strings (lines between 2 ~ 4 sorted):
ajde
8as
ab-sda
AB1-s
-1*aAb
?18. Make dcl recover from input errors.
-
- 和20题属于最后一节复杂的声明,这里程序用了递归下降解析算法,目前还未学习解析(parsing)技术,学习编译原理后补充。