精华内容
下载资源
问答
  • C语言字符串详解

    2021-03-02 11:33:52
    C语言本身没有内置的字符串类型,字符串本质上是一种特殊类型的数组,它的组成元素类型为char,除此之外不受制与数组长度的限制,'\0'作为结束标志,作为字符串结束的标志。(\0作为一个特殊字符,它的ASCII值为0...

    C语言中字符串详解

    字符串时是C语言中非常重要的部分,我们从字符串的性质和字符串的创建、程序中字符串的输入输出和字符串的操作来对字符串进行详细的解析。

    什么是字符串?

    C语言本身没有内置的字符串类型,字符串本质上是一种特殊类型的数组,它的组成元素类型为char,除此之外不受制与数组长度的限制,以'\0'作为结束标志,作为字符串结束的标志。(\0作为一个特殊字符,它的ASCII值为0,但是它不是'0'字符,'0'字符的ASCII值为48。)

    定义字符串

    1. 字符串字面量(字符串常量)

    字符串字面量形如"string",也被称为字符串常量,编译器会将它末尾自动添加上字符串结尾标志\0。它作为一种静态存储类型, 在程序开始运行时被分配地址,一直存在到程序结束,引号括起来的部分将表示它储存的首地址,很类似于数组,数组名作为数组首元素储存的地址。

    #include <stdio.h>
    
    int main() {
    printf("%s  %p   %c", "Hello", "Hello", *"Hello");
    return 0;
    }
    /**
     * Hello  00405044   H
     * **/
    

    上面说明了字符串常量的储存形式,而且它本身只代表首元素的地址。

    2. 字符串数组形式的初始化

    字符串以一种特殊的字符串数组的形式存在,区别于一般数组,进行一般初始化时:

    char a[] = {'h', 'e', 'l', 'l', 'o', '!', '\0'};

    而不能是:

    char a[] = {'h', 'e', 'l', 'l', 'o', '!'};

    后者仍然是一个普通的字符串数组,不是字符串,这样的初始化显然是麻烦的,我们可以这样:

    char a[] = "hello!";

    或者

    char *a = "hello!";

    怎么理解这两种行为呢,他们都使用a储存了字符串hello!的地址,但是它们也是有所不同的,下面详细讨论下他们的区别所在。

    3. 字符串数组和指针

    • 字符串数组形式:我们知道字符串常量以静态形式储存在程序中,使用字符串数组来对它进行存储时需要将其拷贝到新的储存空间,然后将新的储存空间地址赋值到a上。
    • 指针形式:这时候就是一个常规意义上的赋值,我们把在静态储存区的常量地址直接赋值到a上。

    这样本质的区别有什么在应用上的区别呢?其一,使用字符串数组a作为常量指针来储存地址,使用指针形式是一种变量来储存地址;其二,因为字符串数组将是一种对原字符常量的一种拷贝,所以我们支持和允许对这样字符串的修改,但是指针只是对原常量地址的一种储存,我们不允许对常量进行修改,所以通过这个指针对原字符进行修改是未定义的恶劣行为,我们看下面的程序:

    #include <stdio.h>
    
    int main() {
        char *a = "hello!";
        a[0] = 'w';
        printf("%s", "hello!");
    }
    

    这样的程序看起来没问题,我们希望将hello!修改为wello!,然后我们希望打印hello!,但是这样的程序可能输出wello!,因为我们修改了源地址上的数据,当然编译器也有可能崩溃。

    所以一般情况下,我们只希望同过使用常量指针来储存字符串

    const char *a = "hello!";,这样可以避免在程序中出现异常的修改常量的错误。

    所以我们可以总结,我们希望修改字符串时使用字符串数组,只希望读取字符串时我们使用指针,而且应该是常量指针。

    还有一些关于它们值得讨论的部分,假如我们想要使用我们有一个字符串数组(本质上作为一个字符数组的数组),有下面两种形式:

    char a[3][20] = {"I love you.", "Do you love me?", "Please."};

    char *a[3] = {"I love you.", "Do you love me?", "Please."};

    这样又有什么区别呢?第一个字符串数组占用3*20*1 = 60byte,第二个占用3个指针为3*4=12byte。在程序非静态部分无疑是后者更为俭省。而且前者因为固定格式的原因,字符参差不齐但是它们创建的空间却都必须满足容纳最长的字符串,造成一定空间的浪费。

    所以想要使用一系列待显示的字符串时可以使用指针数组,想要修改字符串在之后则使用一般形式的字符串的数组。

    还有对字符串的拷贝,因为字符串变量所存在的形式都是字符串首元素的地址,所以我们下意识对于字符串的拷贝往往是不起作用的:

    #include <stdio.h>
    
    int main() {
     char *a = "Hello!";
     char *pa = a;
     printf("a = %s   %s = pa\n", a, pa);
     printf("a -> %p\n", a);
     printf("pa -> %p\n", pa);
     printf("a = & %p\n", &a);
     printf("pa = & %p\n", &pa);
    }
    
    /**
     a = Hello!   Hello! = pa
     a -> 00405044
     pa -> 00405044
     a = & 0061FF1C
     pa = & 0061FF18
     * **/
    

    在这里a和pa作为字符串打印时,内容时完全相同的,但是仔细看我们发现他们起始指向了相同的地址,也就是所我们并没有完成对字符串内容的拷贝,而只是对地址值的拷贝,而且a和pa作为指针储存在相邻的两个单元,相隔4个字节。这样的拷贝在某些意义上不大,我们将在下面再讨论如何对于字符串进行拷贝。

    字符串I/O

    首先,在了解了字符串性质的情况下,我们来了解字符串I/O,因为字符串需要在创建时获得一段连续的数组空间,所以尝试将输入的字符串加载进入程序时,我们需要先 分配空间

    这样做是必要的,因为对于未分配内存的字符指针,我们并不知道它的初始状态,它可能指向任意位置,我们在进行输入的时候很有可能因此抹除了先前储存位置上的数据,通常这是不被编译器允许的,往往会造成程序崩溃。

    #include <stdio.h>
    
    int main() {
     char *a;
     scanf("%s", a);// 这个程序可能会崩溃
     puts(a);
     return 0;
    }
    
    

    所以在处理字符串I/O之前,首先要考虑的就是为输入的字符串分配空间,而且保证输入的字符串不超过我们申请的空间。

    下面我们来看一些I/O函数来深入理解这样的理念。

    1. gets()被废弃的选项

    gets(),gets需要一个参数,一个字符串指针,它从I/O设备上读取一行信息(等到遇到一个换行符停止),然后在末尾添加空字符,最后的换行符也会被读取并丢弃。

    看起来这是一个很不错的I\O函数,但是在C99标准中它被建议不要使用,在C11标准中被完全废弃,这是因为它存在着严重的隐患,看下面这段程序:

    #include <stdio.h>
    
    int main() {
     char b[5] = "hhhh";
     char a[5];
     gets(a);
     puts(a);
     puts(b);
     return 0;
    }
    
    /**
     abcd
     abcd
     hhhh
     * **/
    
    /**
        abcdefghijklmn
        abcdefghijklmn
        fghijklmn
     **/
    

    这段程序,我们输入了两段内容进行测试,第一次abcd刚好长度为5,gets函数正常接受将它放到a分配的地址中,没有出现问题;但是在第二次我们输入了超过了既定分配长度的字符,我们发现原字符出现了异常的变化,超过了既定长度5,容纳下了所有的输入字符,但是随之我们原有的字符串b也被完全修改,原数据被完全抹除。

    这是因为它们的地址刚好相邻,gets函数并不会对字符长度进行检查,它只会将一整行的数据放入指针指向区域上,即使超过申请空间的边界,他也会继续写入,抹去相邻区域的数据也在情理之中了。

    这给我们程序带来了巨大的危险,如果溢出的部分占用了未使用的空间问题并不大,但是它轻易抹除以使用空间中内容很可能导致程序崩溃,所以我们不要使用gets函数,应该尝试更多的根据建议使用fgets()或则gets_s来避免这样的问题。

    2. fgets()和gets_s()

    为了解决gets函数中存在的问题,有两个可以函数作为替代。

    首先是基于gets的升级版gets_s他需要另外的一个参数指定最大读取长度,并根据这个长度来做出相对应的操作:

    • 正常情况下,gets_s从标准输入流中读取信息,类似于gets在未达到最大长度而且读取到换行符时,它将从缓冲区读取该换行符并将其丢弃并在末尾补充上空字符。
    • 在读取出现问题时,gets_s读取到最大读取长度数目的字符但是仍然未读取到换行符时,gets_s将会将对应指针(数组首字符)指向数据设定为空字符,然后继续读取知道读取到文件末尾或者换行符,然后返回空指针,之后调用依赖实现的函数的处理函数,可能中止或者退出程序。

    在这里我们给出一段处理函数使用的实例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <crtdbg.h>  // For _CrtSetReportMode
    
    void myInvalidParameterHandler(const wchar_t* expression,
                                   const wchar_t* function,
                                   const wchar_t* file,
                                   unsigned int line,
                                   uintptr_t pReserved)
    {
        wprintf(L"Invalid parameter detected in function %s."
                L" File: %s Line: %d\n", function, file, line);
        wprintf(L"Expression: %s\n", expression);
        printf("Error!");
    }
    
    int main() {
        char a[5];
    
        _invalid_parameter_handler oldHandler, newHandler;
        newHandler = myInvalidParameterHandler;
        oldHandler = _set_invalid_parameter_handler(newHandler);
        _CrtSetReportMode(_CRT_ASSERT, 0);
        gets_s(a, 5);
        puts(a);
        return 0;
    }
    

    在这里及时我们输入超过5位字符,程序也不会呈现崩溃退出。

    详细信息参照

    我们发现gets_s函数中使用并不特别方便,还有一个函数可以作为替代fgets函数,它相较于前两者,还需要另外一个参数,读入文件名称,如果从键盘中读取,那么即为标准输入流stdinfgets函数的一般行为:

    • 正常读取到换行符或则文件末尾时,读取停止,将换行符读入字符串中然后在字符串末尾上填入空字符。这时候函数会返回指向读取函数储存位置的指针,如果到达文件末尾将返回空指针,当读取发生某些其他错误时也会返回空指针,在C语言中它被定义为宏NULL
    • 在读取超过字符串最大长度的字符时,将要达到最大长度时停止读取然后在末尾补充上空字符。在读取到文件末尾时函数会返回空指针。
    #include <stdio.h>
    
    int main() {
        char a[5];
        char *status = fgets(a, 5, stdin);
        puts(a);
        printf("a = &%p\tstatus = &%p\n", a, status);
        return 0;
    }
    
    
    /**
        123456
        123
        a = &0135FA10   status = &0135FA10
     * **/
    
    /**
        123
        123
    
        a = &0061FF17   status = &0061FF17
     **/
    

    对于最大长度参数n,表明函数最多读取n-1个数据(包括换行符),所以输入123会将换行符正常读取然后puts函数又输出了一个换行符,所以输出了两个换行符;但是输入更多时,函数读取到四个数据后停止读取补充上空字符。

    不同于gets_s函数,读取不到换行符时,函数也不会对缓冲区中其他数据做出任何操作,对于前者会清空缓冲区中所有下一个换行符前的所有内容,但是fgets并不会,我们可以自由的选择对这些缓冲区的内容进行处理。

    由于fgets()函数的安全性和可扩展性更佳,所以我们推荐更多的去使用fgets()函数。它往往是最佳选择。

    3. scanf()不甚理想的选择

    scanf作为泛用性很强的函数,也有它读取字符串的模式:

    scanf("%s", a);

    但是使用它来读取字符串并不是最理想的选择,因为scanf函数读取字符时开始与一个非空字符,终止于第一个空字符。这样下来他可能只可以读取到一个简单的单词,而不是我们期望的包含空格等完整内容的字符串,所以一般情况下我们不使用scanf读取整句字符串,而将它用于单词和具有特定格式的字符的读取。

    我们可以通过转换说明修饰符来读取规则的字符串:

    scanf("%5s", a);这样就可以读取长度为5的单词(中间读取到换行符依旧会停止读取,其中不包括空字符),功能可以比拟fgets(a, 6, stdin);,但是后者可能包括特殊的换行符之类,所以它们也算是各有用武之地。

    4. 输出函数

    系统的说明了几个C语言输入函数,我们现在来类似的梳理输出函数,它们与输入函数是相对应的,也是各有特色的。

    • puts——gets,输出字符串直到空字符,并且会在最后输出一个换行符,这样的存在也可能访问到未被分配的内存,这样的行为是未定义的,但是这样很不靠谱。
    • fputs——fgets,输出字符直到碰到空字符,但是与fgets匹配,它不会在输出最后输出换行符,而且需要额外的参数指示输出位置,如果是屏幕则为stdout
    • printf——scanfscanf相较于前两者较为多才多艺,不会输出换行符,可以根据自己对格式的要求进行自由控制,而且在同时输出多个字符时用起来十分方便。

    字符串处理函数

    讨论完字符串性质和I\O后我们来继续讨论和字符串息息相关的一些C语言自带的字符串处理函数(其中大部分都是我们可以实现的),熟悉他们方便我们更好的处理字符串。一般情况下他们定义在头文件string.h中。

    1. strcatstrncat

    这两个函数被用来字符串合并。

    对于strcat接受两个字符串指针作为参数,将第二个字符串接到第一个字符串上,然后返回第一个字符串的指针,但是它也存在类似gets的缺陷,当第一个指针所指向被分配的空间并不足够大时,额外从第二个字符读取的字符将会可能覆写掉其他已经分配空间上的数据。但是基于C语言制定时相信程序员的准则它仍然可以继续使用,不同于getsgets产生的错误可能由用户制造,但是strcat制造的问题却可以由程序员来避免,所以它仍然可以使用。

    strncat需要额外的一个指定拷贝后的字符的最大长度(包含空字符),以此来保证拷贝后的数组不会超过以分配的储存空间,其他内容同strcat一致。

    1. strcmpstrncmp

    这两个函数用于字符串比较。

    对于strcmp,接受两个字符串指针比较它们指向的字符串(而不是它们所指向的地址)如果相同则返回0,否则返回非零的数字,具体情况根据编译器的实现有所不同。

    strcmp也可以通过指定从指定的起始位置开始比较字符串,只需要在传递指针时进行加减运算:

    strcmp(a+5, b+4);这样使得字符串的比较更加灵活。

    strncmp使得字符串的比较更加灵活,通过第三个参数n来指定比较的长度,我们可以进行前缀匹配。

    1. strcpystrncpy

    这两个函数用于字符串的拷贝。

    strcpy拷贝第二个字符串指针的字符到第一个字符串指针所指向的空间中去,但是我们也需要注意第一个参数所指向的空间也必须足够大容纳第二个字符串。我们也大可不必从字符开始部分开始拷贝,我们可以吧参数指针移动到任何我们想要它拷贝到的位置:

    strcpy(a+4, "hello!");

    strncpy弥补了strcpy的缺点,可以在第三个参数中指定拷贝的最大长度(这个大小不包含空字符,因为函数设计就预想到可能碰不到空字符就要停止,所以拷贝完这个最大长度后,函数会向原字符后自动添加上空字符),但是n的大小最大为第一个字符数组空间大小减去1。

    1. sprintf

    sprintf声明在stdio.h中,类似于printf它可以将字符串进行格式化并输出到一个字符串中,使用时同样需要考虑字符串分配空间的问题,这个问题在所有涉及字符串的使用时都要考虑!下面看一段用例:

    #include <stdio.h>
    
    int main() {
        char *s = "Today is ";
        int year = 2021, month = 2, day = 2;
        char data[30];
        sprintf(data, "%s%d/%d/%d.", s, year, month, day);
        puts(data);
        return 0;
    }
    
    
    /**
       Today is 2021/2/2.
     * **/
    

    总结

    总的来说字符串使用时,无论在何时务必用注意分配空间的使用,不要访问到未分配的空间,这样会给程序带来无法预料的结果。

    展开全文
  • varchar2 1~4000字节 可变长度字符串,与CHAR类型相比,使用VARCHAR2可以节省磁盘空间,但查询效率没有char类型高 数值类型 Number(m,n) m(1~38) n(-84~127) 可以存储正数、负数、零、定点数和精度为38位的浮点数...
  • 因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。  本章介绍数值数组和字符数组,其余的在以后各章陆续介绍。数组类型说明 在C语言中使用数组必须先进行类型说明。 ...
  • 3.从键盘输入一个字符串,将其中的小写字母全部转换成大写字母,然后输出到一个磁盘文件test中保存,输入字符串以“!”结束。4.有两个磁盘文件A和B,各存放一行字母,今要求把这两个文件中的信息合并(按字母顺序...

    第五版谭浩强课后答案 第十章《对文件的输入输出​》习题答案

    1.什么是文件型指针?通过文件指针访问文件有什么好处?

    答:缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名为FILE。

    通过文件指针访问文件的好处是:可以随机访问文件,有效表示数据结构,动态分配内存,方便使用字符串,有效使用数组。

    2.对文件的打开与关闭的含义是什么?为什么要打开和关闭文件?

    答:”打开“是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输人输出的数据)。

    ”关闭“是指撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,显然就无法进行对文件的读写了。

    3.从键盘输入一个字符串,将其中的小写字母全部转换成大写字母,然后输出到一个磁盘文件test中保存,输入的字符串以“!”结束。

    #include <stdio.h>
    #include <stdlib.h>
    
    int main( void ) {
    	FILE *fp = NULL;
    	char c;
    	int i;
    	
    	if ( (fp=fopen("test", "w")) == NULL ) {
    		printf("open file test error!\n");
    		exit(EXIT_FAILURE);
    	}
    
    	while ( (c=getchar()) != EOF && c != '!' ) {
    		if ( c>='a' && c<='z' )
    			c = c-'a' + 'A';
    		fputc(c, fp);
    	}
    
    	fclose(fp);
    }
    

    结果:

    输入 : 123我的AbcABC!
    test文件的内容 : 123我的ABCABC
    

    4.有两个磁盘文件A和B,各存放一行字母,今要求把这两个文件中的信息合并(按字母顺序排列),输出到一个新文件C中去。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void swap(char *s, int i, int j) {
        char t = s[i];
        s[i] = s[j];
        s[j] = t;
    }
    
    void select_sort(char *str) {
        int i, j;
        int len = strlen(str);
        for (i=0; i<len; i++) {
            int min = i;
            for (j=i+1; j<len; j++) {
                if ( str[j] < str[min] )
                    min = j;
            }   
            swap(str, min, i); 
        }   
    }
    
    int main( void ) { 
        FILE *fa, *fb, *fc;
        char buf[1024] = {0};
    
        fa = fopen("A", "r");
        fb = fopen("B", "r");
        fc = fopen("C", "w");
    
        fgets(buf, 1024, fa);
        int len = strlen(buf);
        fgets(buf+len, 1024-len, fb);
        select_sort(buf);
        fputs(buf, fc);
    
        fclose(fa);
        fclose(fb);
        fclose(fc);
    }
    

    5.有5个学生,每个学生有3门课程的成绩,从键盘输人学生数据(包括学号,姓名,3门课程成绩),计算出平均成绩,将原有数据和计算出的平均分数存放在磁盘文件stud中。

    #include <stdio.h>
    #include <stdlib.h>
    
    struct student {
        int num;
        char name[32];
        int score[3];
        float avg;
    };
    
    int main( viod ) { 
        int i;
        struct student stu[5];
        FILE *fp = NULL;
            
        for (i=0; i<5; i++) {
            printf("num name score1 score2 score3:\n");
            scanf("%d %s %d %d %d", &stu[i].num, &stu[i].name, 
                &stu[i].score[0],&stu[i].score[1],&stu[i].score[2]);
            stu[i].avg = (stu[i].score[0]+stu[i].score[1]+stu[i].score[2])/3.0;
        }   
    
        if ( (fp=fopen("stud", "wb")) == NULL ) { 
            printf("open file stud for write error\n");
            return 1;
        }
    
        if ( fwrite(stu, sizeof(stu), 1, fp) != 1 ) {
            printf("write error\n");
            return 1;
        }
        fclose(fp);
    }
    

    测试程序查看输入文件的容:

    1 zhagnsan 10 20 30 20.000000
    2 lisi 3 6 9 6.000000
    3 wangwu 1 2 3 2.000000
    4 zhaoliu 90 80 10 60.000000
    5 sunqi 90 1 1 30.000000
    

    6.将第5题stud文件中的学生数据,按平均分进行排序处理,将已排序的学生数据存入一个新文件stu_ sort 中。

    #include <stdio.h>
    #include <stdlib.h>
    
    struct student {
        int num;
        char name[32];
        int score[3];
        float avg;
    };
    
    void sort(struct student stu[], int len) {
        int i, j;
        struct student tmp;
        for (i=0; i<len; i++) {
            int min = i;
            for (j=i+1; j<len; j++) {
                if ( stu[j].avg > stu[min].avg )
                    min = j;
            }   
            tmp = stu[min];
            stu[min] = stu[i];
            stu[i] = tmp;
        }   
    }
    
    int main( viod ) {
        int i;
        struct student stu[5];
        FILE *fp = NULL;
        if ( (fp=fopen("stud", "rb")) == NULL ) {
            printf("open file stud for read error\n");
            return 1;
        }
    
        if ( fread(stu, sizeof(stu), 1, fp) != 1 ) {
            printf("write error\n");
            return 1;
        }
        fclose(fp);
    
        sort(stu, 5);
    
        FILE *fw = fopen("stu_sort", "wb");
        fwrite(stu, sizeof(stu), 1, fw);
        fclose(fw);
    }
    

    测试程序,查看文件内容,确实排过序:

    4 zhaoliu 90 80 10 60.000000
    5 sunqi 90 1 1 30.000000
    1 zhagnsan 10 20 30 20.000000
    2 lisi 3 6 9 6.000000
    3 wangwu 1 2 3 2.000000
    

    7.将第6题已排序的学生成绩文件进行插人处理。插人一个学生的3门课程成绩,程序先计算新插人学生的平均成绩,然后将它按成绩高低顺序插入,插入后建立一个新文件。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct student {
        int num;
        char name[32];
        int score[3];
        float avg;
    };
    
    void sort(struct student stu[], int len) {
        int i, j;
        struct student tmp;
        for (i=0; i<len; i++) {
            int min = i;
            for (j=i+1; j<len; j++) {
                if ( stu[j].avg > stu[min].avg )
                    min = j;
            }   
            if ( min != i ) { 
                tmp = stu[min];
                stu[min] = stu[i];
                stu[i] = tmp;
            }   
        }   
    }
    int main( viod ) { 
        int i;
        struct student stu[5];
        FILE *fp = NULL;
        if ( (fp=fopen("stu_sort", "rb")) == NULL ) {
            printf("open file stud for read error\n");
            return 1;
        }
    
        if ( fread(stu, sizeof(stu), 1, fp) != 1 ) {
            printf("write error\n");
            return 1;
        }
        fclose(fp);
    
        struct student new_stu[6];
        memcpy(new_stu, stu, sizeof(stu));
        printf("num name score0 score1 score2:\n");
        scanf("%d %s %d %d %d", &new_stu[5].num, &new_stu[5].name, &new_stu[5].score[0],
                &new_stu[5].score[1], &new_stu[5].score[2]);
        new_stu[5].avg = (new_stu[5].score[0]+new_stu[5].score[1]+new_stu[5].score[2])/3.0;
        sort(new_stu, 6);
    
        FILE *fw = fopen("tmp_sort", "wb");
        fwrite(new_stu, sizeof(new_stu), 1, fw);
        fclose(fw);
    }
    

    查看tmp_sort文件,确实插入和排序了:

    4 zhaoliu 90 80 10 60.000000
    5 sunqi 90 1 1 30.000000
    1 zhagnsan 10 20 30 20.000000
    8 hehe 12 3 4 6.333333
    2 lisi 3 6 9 6.000000
    3 wangwu 1 2 3 2.000000
    

    8.将第7题结果仍存人原有的stu_sort 文件而不另建立新文件。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct student {
        int num;
        char name[32];
        int score[3];
        float avg;
    };
    
    int main( viod ) { 
        int i;
        struct student stu[6];
        FILE *fp = NULL;
        if ( (fp=fopen("tmp_sort", "rb")) == NULL ) { 
            printf("open file stud for read error\n");
            return 1;
        }   
    
        if ( fread(stu, sizeof(stu), 1, fp) != 1 ) { 
            printf("write error\n");
            return 1;
        }   
        fclose(fp);
    
        FILE *fw = fopen("stu_sort", "wb");
        fwrite(stu, sizeof(stu), 1, fw);
        fclose(fw);
    }
    

    查看原本的stu_sort文件:

    4 zhaoliu 90 80 10 60.000000
    5 sunqi 90 1 1 30.000000
    1 zhagnsan 10 20 30 20.000000
    8 hehe 12 3 4 6.333333
    2 lisi 3 6 9 6.000000
    3 wangwu 1 2 3 2.000000
    

    9.有一磁盘文件employee,内存放职工的数据。每个职工的数据包括职工姓名、职工号、性别、年龄、住址、工资、健康状况、文化程度。今要求将职工名、工资的信息单独抽出来另建一个简明的职工工资文件。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct employee {
        int  num;      // 编号
        char name[32];
        char sex[4]; 
        int  age;
        char addr[60];
        int  salary;   
        char health[10]; // 健康状况
        char class[10];  // 文化程度
    };
    
    struct emp {
        char name[32];
        int salary;
    };
    
    int main( void ) { 
        int i;
        FILE *fp1, *fp2; 
        struct emp emp_arr[5];
        struct employee employee_arr[5];
    
        fp1=fopen("employee", "rb");
        fread(employee_arr, sizeof(employee_arr), 1, fp1);
        fclose(fp1);
    
        for (i=0; i<5; i++) {
            strcpy(emp_arr[i].name, employee_arr[i].name);
            emp_arr[i].salary = employee_arr[i].salary;
        }
    
        fp2=fopen("emp", "wb");
        fwrite(emp_arr, sizeof(emp_arr), 1, fp2);
        fclose(fp2);
    }
    

    查看emp文件的内容如下:

    abc 1800 
    def 2000 
    hehe 3000 
    haha 2800 
    ggg 2500 
    

    10.从第9题的“职工工资文件”中删去一个职工的数据,再存回原文件。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct emp {
        char name[32];
        int salary;
    };
    
    int main( void ) { 
        int i;
        FILE *fp;
        char name[32]; 
        struct emp emp_arr[5];
    
        fp=fopen("emp", "rb");
        fread(emp_arr, sizeof(emp_arr), 1, fp);
        fclose(fp);
    
        printf("name:");
        scanf("%s", &name);
        fp=fopen("emp", "wb");
        for (i=0; i<5; i++) {
            if ( strcmp(emp_arr[i].name, name) == 0 ) { 
                continue;
            }   
            fwrite(&emp_arr[i], sizeof(emp_arr[i]), 1, fp);
        }   
        fclose(fp);
    }
    

    删除ggg后的源文件内容:

    abc 1800 
    def 2000 
    hehe 3000 
    haha 2800 
    

    11.从键盘输人若干行字符(每行长度不等),输人后把它们存储到一磁盘文件中。再从该文件中读入这些数据,将其中小写字母转换成大写字母后在显示屏上输出。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main( void ) { 
        int i;
        FILE *fp = fopen("tmp.txt", "w");
        char buf[1024] = {}; 
        
        while ( fgets(buf, 1024, stdin) != NULL ) { 
            fputs(buf, fp);
            memset(buf, 0x00, sizeof(buf));
        }   
        fclose(fp);
    
        fp = fopen("tmp.txt", "r");
        while ( !feof(fp) ) { 
            memset(buf, 0x00, sizeof(buf));
            fgets(buf, 1024, fp);
            for (i=0; buf[i] != '\0'; i++) {
                if ( buf[i]>='a' && buf[i]<='z' )
                    printf("%c", buf[i]-32);
                else
                    printf("%c", buf[i]);
            }   
        }   
        fclose(fp);
    }
    

    执行结果:

    输入:
    this is maomaochong
    litao love IDFD
    1243
    输出:
    THIS IS MAOMAOCHONG
    LITAO LOVE IDFD
    1243
    

    看到这里了,如果有帮助,记得点赞评论

    其他章节:
    第一章《程序设计和C语言》习题答案
    第二章《算法–程序的灵魂》习题答案
    第三章《最简单的C程序设计–顺序程序设计》习题答案
    第四章《选择结构程序设计》习题答案
    第五章《循环结构程序设计》习题答案
    第六章《利用数组处理批量数据》习题答案
    第七章《用函数实现模块化程序设计​》习题答案
    第八章《善于利用指针​》习题答案
    第九章《用户自己建立数据类型​》习题答案
    第十章《对文件的输入输出​》习题答案

    展开全文
  • C语言的科学和艺术.pdf

    热门讨论 2012-01-19 14:09:05
    C语言的科学和艺术》介绍ANSI C为主线,不仅涵盖C语言的基本知识,而且介绍了软件工程技术以及如何应用良好的程序设计风格进行开发等内容。《C语言的科学和艺术》采用了库函数的方法,强调抽象的原则,详细阐述...
  • 吃火锅的C语言解法

    千次阅读 2020-12-12 21:00:58
    输入每行给出一句不超过 80 个字符的、回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。 ————...

    转载自pta

    以上图片来自微信朋友圈:这种天气你有什么破事打电话给我基本没用。但是如果你说“吃火锅”,那就厉害了,我们的故事就开始了。

    本题要求你实现一个程序,自动检查你朋友给你发来的信息里有没有 chi1 huo3 guo1。

    输入格式:
    输入每行给出一句不超过 80 个字符的、以回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。

    ——————————————————————

    输出格式:
    首先在一行中输出朋友信息的总条数。然后对朋友的每一行信息,检查其中是否包含 chi1 huo3 guo1,并且统计这样厉害的信息有多少条。在第二行中首先输出第一次出现 chi1 huo3 guo1 的信息是第几条(从 1 开始计数),然后输出这类信息的总条数,其间以一个空格分隔。题目保证输出的所有数字不超过 100。

    如果朋友从头到尾都没提 chi1 huo3 guo1 这个关键词,则在第二行输出一个表情 -_-#。

    输入样例 1:
    Hello!
    are you there?
    wantta chi1 huo3 guo1?
    that’s so li hai le
    our story begins from chi1 huo3 guo1 le
    .
    输出样例 1:
    5
    3 2

    ——————————————————————

    输入样例 2:
    Hello!
    are you there?
    wantta qi huo3 guo1 chi1huo3guo1?
    that’s so li hai le
    our story begins from ci1 huo4 guo2 le
    .
    输出样例 2:
    5
    -_-#

    #include<stdio.h>
    #include<string.h>
    int main()
    {
        int num1=0,num2=0,t=0,i;
        char ch1[]="chi1 huo3 guo1";
        char ch2[999];
        for(i=1;i<=120;i++){
            gets(ch2);
            if(ch2[0]=='.'&&strlen(ch2)==1)break;
            else{
                num1++;
                    if(strstr(ch2,ch1)!=0){
                        num2++;
                        if(t==0)t=num1;
                    }
            }
        }
        if(num2)printf("%d\n%d %d",num1,t,num2);
        else printf("%d\n-_-#",num1);
        return 0;
    }
    
    

    使用函数:

    strstr
    包含在string.h头文件中
    定义:char *strstr(const char *str1, const char *str2);
    功能:返回指向字符串str2第一次出现在字符串str1中的位置的指针;如果str1中不包含str2,则返回NULL。

    如有错误,欢迎指正

    展开全文
  • L1-070 吃火锅(C语言)

    2021-05-11 19:42:37
    输入每行给出一句不超过 80 个字符的、回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。 输出格式: ...

    L1-070 吃火锅(C语言)

    题目描述:

    在这里插入图片描述

    以上图片来自微信朋友圈:这种天气你有什么破事打电话给我基本没用。但是如果你说“吃火锅”,那就厉害了,我们的故事就开始了。
    本题要求你实现一个程序,自动检查你朋友给你发来的信息里有没有 chi1 huo3 guo1

    输入格式:

    输入每行给出一句不超过 80 个字符的、以回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。

    输出格式:

    • 首先在一行中输出朋友信息的总条数。
    • 然后对朋友的每一行信息,检查其中是否包含 chi1 huo3 guo1,并且统计这样厉害的信息有多少条。
    • 在第二行中首先输出第一次出现 chi1 huo3 guo1 的信息是第几条(从 1 开始计数)
    • 然后输出这类信息的总条数,其间以一个空格分隔。题目保证输出的所有数字不超过100。
    • 如果朋友从头到尾都没提 chi1 huo3 guo1 这个关键词,则在第二行输出一个表情 -_-#

    输入样例 1:

    Hello!
    are you there?
    wantta chi1 huo3 guo1?
    that’s so li hai le
    our story begins from chi1 huo3 guo1 le
    .

    输出样例 1:

    5
    3 2

    输入样例 2:

    Hello!
    are you there?
    wantta qi huo3 guo1 chi1huo3guo1?
    that’s so li hai le
    our story begins from ci1 huo4 guo2 le
    .

    输出样例 2:

    5
    -_-#

    解题思路

    循环接收输入的每一条信息,直到遇见单独的 . ,输入结束。每接收一条信息,判断该字符串中是否存在 chi1 huo3 guo1 这个字串,存在则修改相应的值,不存在则接收下一条信息。

    易错分析

    1. 每一条信息接收一次,因为可能存在空格,因此不能使用scanf()函数接收,应使用gets()函数
    2. 每一条信息只判断是否存在字串,不需要判断存在几个字串 (测试点 2)
    3. 单独的 . 是结束标志,但可能存在 . 开头的信息,此时没有结束,仍需进行判断,并继续接收下一条信息 (测试点 3)

    解题方法

    本题重点就在字符串中查找子串的方法

    方法一:

    使用strstr()函数
    char *p = strstr(t1,t2),若t2t1的字串,返回值不为空,反之,返回值为空

    用法举例:

    char t1[] = "abcdefgh";
    char t2[] = "def";
    char *p = strstr(t1,t2);
    //结果
    p = "defgh"
    ---------------------------------------------------
    char t1[] = "abcdefgh";
    char t2[] = "xyz";
    //结果
    p = NULL
    

    方法2:

    使用strncmp()函数
    strncmp(t1,t2,n),比较t1t2的n个字符,若返回值为0,则两字符串的n个字符相同
    【】注意,t1t2代指字符串开始的位置,即
    strncmp(t1,t2,n)指的是t1t2两个字符串从头开始比较n个字符
    strncmp(&t1[3],&t2[5],4)指的是t1[3]开始的4个字符与t2[5]开始的4个字符进行比较

    题目代码

    方法1和方法2取其一即可

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
    	char temp[81] = { 0 };
    	char t[] = "chi1 huo3 guo1";
    	int tiaoshu = 0;  //朋友发来的信息条数
    	int first = 0;  //第一条出现的位置,为0则没有要的信息
    	int cishu = 0;  //吃火锅出现的总次数
    
    	while (1)
    	{
    		gets(temp);
    
    		if ('.' == temp[0] && strlen(temp) == 1)  //跳出循环的条件
    			break;
    
    		tiaoshu++;
    		
    		//方法1
    		char* p = strstr(temp, t);
    		if (p != NULL)
    		{
    			if (0 == first)
    				first = tiaoshu;
    			cishu++;
    		}
    
    		//方法2
    		//if (strlen(temp) >= 14)
    		//{
    		//	for (int i = 0; i <= strlen(temp) - 14; i++)
    		//	{
    		//		if (0 == strncmp(&temp[i], t, 14))
    		//		{
    		//			if (0 == first)
    		//				first = tiaoshu;
    		//			cishu++;
    		//			break;   //防止出现多次吃火锅的信息
    		//		}
    		//	}
    		//}
    	}
    
    	//输出
    	printf("%d\n", tiaoshu);
    	if (0 == first)
    		printf("-_-#");
    	else
    		printf("%d %d", first, cishu);
    
    	return 0;
    }
    
    展开全文
  • 编译原理 C语言实现词法分析

    热门讨论 2011-05-14 00:48:46
    这里开始定义的C语言子集的源程序作为词法分析程序的输入数据。在词法分析中,自文件头开始扫描源程序字符,一旦发现符合“单词”定义的源程序字符串时,将它翻译成固定长度的单词内部表示,并查填适当的信息表。 ...
  • 9-1 什么字符串 266 字符串字面量 266 字符串字面量的长度 266 字符串 268 字符数组的初始化赋值 269 空字符串 270 字符串的读取 270 格式化显示字符串 271 9-2 字符串数组 273 字符串数组 273 读取...
  •  10.7.2 使用scanf( )函数输入字符串 152  10.8 总结 154  10.9 问与答 154  10.10 作业 155  10.10.1 小测验 155  10.10.2 练习 156  第11天课程 结构、共用体和TypeDef 157  11.1 简单结构 157  ...
  • 输入每行给出一句不超过 80 个字符的、回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。 输出格式: ...
  • PTA 7-18 吃火锅c语言

    2021-02-21 18:27:02
    输入每行给出一句不超过 80 个字符的、回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。 输出格式: ...
  • 定义一个二维数组,输入一句话,然后以字符串的形式把每个单词分别存储在数组的每一行,这个循环该 怎么写,判断结束的标准是什么(不清楚单词多少和大小),但知道它的限制。
  • 10.3.8 从键盘上输入字符串 383 10.3.9 键盘的非格式化输入 384 10.4 屏幕输出 389 10.4.1 使用printf()格式输出到屏幕 389 10.4.2 转义序列 391 10.4.3 整数输出 392 10.4.4 输出浮点数 394 10.4.5 字符...
  • 输入串字符以‘#’结束,如果它是大写字母,则转换为小写字母输出,如果是小写字母,则转换为大写字母输出,如果不是字母,则照直输出 while((ch=getchar())!="#") {if(ch>="A"&&ch) ch=ch-"A"+"a"; else if(ch>=...
  • 1.基本要求 能够实现如下功能: 首先用蓝色清屏 在屏幕中央显示由字符串“-============#”组成的黄色的小球,#为球, 按下方向键可以控制上述小球球行方式在屏幕上行走 在行进过程中,球只能左转、右转或...
  • //将末尾增加字符串结束标志 strcat(cP_File_Name,argv[2]);//连接输入字符串,形成 "cd 目录"的格式 system(cP_File_Name);//使用system函数切换至指定目录 if (argc == NOCHANGE)//如果参数只有3个,即...
  • 电话薄管理系统C语言

    2010-12-16 18:31:55
    /*输入字符串,并进行长度验证(长度)*/ void stringinput(char *t,int lens,char *notice) { char n[255]; do{ printf(notice); /*显示提示信息*/ scanf("%s",n); /*输入字符串*/ if(strlen(n)>lens) printf("\...
  • 输入每行给出一句不超过 80 个字符的、回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。 输出格式: ...
  • printf("请输入一个准备存储到磁盘的字符串#结束):"); ch = getchar(); while(ch != '#') { fputc(ch,fp); putchar (ch); ch = getchar(); } fclose(fp); putchar (10); return 0; }
  • printf("请输入一个准备存储到磁盘的字符串#结束):"); ch = getchar(); while(ch != '#') { fputc(ch,fp); putchar (ch); ch = getchar(); } fclose(fp); putchar (10); return 0; }
  • 输入每行给出一句不超过 80 个字符的、回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 . 时,输入结束,此行不算在朋友信息里。 输出格式: ...
  • 意思是一样,都是用于判断是否为null(空),但是假如你du用不同的输入在计算机zhi上,null== a 识别度更dao高,编写代码中null...C语言把它作为字符串的最后一个字符,表示字符串到此结束"\0"。 2.NULL指针是一个无.
  • 9.9 字符串和数组有什么不同? 第10章 位(bit)和字节(byte) 10.1 用什么方法存储标志(flag)效率最高? 10.2 什么是“位屏蔽(bit masking)”? 10.3 位域(bit fields)是可移植的吗? 10.4 移位和乘以2这两...
  • 8.8编写一函数,有实参传来一个字符串,统计此字符串中字母,数字,空格和其它字符的个数,在主函数中输入字符串以及输出上述的结果。 52 8.10写一函数,用“起泡法”对输入的10个字符按由小到大的顺序排列。 54 ...
  • 编译原理 词法分析器

    热门讨论 2010-05-05 22:51:50
    这里开始定义的C语言子集的源程序作为词法分析程序的输入数据。在词法分析中,自文件头开始扫描源程序字符,一旦发现符合“单词”定义的源程序字符串时,将它翻译成固定长度的单词内部表示,并查填适当的信息表。...
  • 第4章 数组和null结束字符串 4.1 一维数组 4.2 生成指向数组的指针 4.3 向函数传递一维数组 4.4 null结束字符串 4.5 二维数组 4.6 多维数组 4.7 带下标的指针 4.8 数组初始化 4.9 棋盘游戏实例 第5章 指针 ...

空空如也

空空如也

1 2 3
收藏数 49
精华内容 19
关键字:

c语言输入字符串以什么结束

c语言 订阅