当前位置: 首页 > news >正文

第11章 字符串和字符串函数

本章介绍以下内容:
函数:gets()、gets_s()、fgets()、puts()、fputs()、strcat()、strncat()、strcmp()、strncmp()、strcpy()、strncpy()、sprintf()、strchr()
创建并使用字符串
使用C库中的字符和字符串函数,并创建自定义的字符串函数
使用命令行参数
字符串是C语言中最有用、最重要的数据类型之一。虽然我们一直在使用字符串,但是要学的东西还很多。C 库提供大量的函数用于读写字符串、拷贝字符串、比较字符串、合并字符串、查找字符串等。通过本章的学习,读者将进一步提高自己的编程水平。

11.1 表示字符串和字符串I/O

字符串是以空字符(\0)结尾的char类型数组。

puts()函数也属于stdio.h系列的输入/输出函数。但是,与printf()不同的是,puts()函数只显示字符串,而且自动在显示的字符串末尾加上换行符。

11.1.1 在程序中定义字符串

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

用双引号括起来的内容称为字符串字面量(string literal),也叫作字符串常量(string constant)。

双引号中的字符和编译器自动加入末尾的\0字符,都作为字符串储存在内存中

从ANSI C标准起,如果字符串字面量之间没有间隔,或者用空白字符分隔,C会将其视为串联起来的字符串字面量。

如果要在字符串内部使用双引号,必须在双引号前面加上一个反斜杠(\)

字符串常量属于静态存储类别(static storage class),这说明如果在函数中使用字符串常量,该字符串只会被储存一次,在整个程序的生命期内存在,即使函数被调用多次。用双引号括起来的内容被视为指向该字符串储存位置的指针。这类似于把数组名作为指向该数组位置的指针。

printf()将打印该字符串首字符的地址(如果使用ANSI之前的实现,可能要用%u或%lu代替%p)

*"space farers"表示该字符串所指向地址上储存的值,应该是字符串*"space farers"的首字符。

2.字符串数组和初始化

一种方法是用足够空间的数组储存字符串。

const char m1[40] = "Limit yourself to one line's worth.";
const表明不会更改这个字符串。
这种形式的初始化比标准的数组初始化形式简单得多:

注意最后的空字符。没有这个空字符,这就不是一个字符串,而是一个字符数组。

在指定数组大小时,要确保数组的元素个数至少比字符串长度多1(为了容纳空字符)。所有未被使用的元素都被自动初始化为0(这里的0指的是char形式的空字符,不是数字字符0)

让编译器计算数组的大小只能用在初始化数组时。如果创建一个稍后再填充的数组,就必须在声明时指定大小。声明数组时,数组大小必须是可求值的整数。在C99新增变长数组之前,数组的大小必须是整型常量,包括由整型常量组成的表达式。

char crumbs[n];     // 在C99标准之前无效,C99标准之后这种数组是变长数组

字符数组名和其他数组名一样,是该数组首元素的地址。

还可以使用指针表示法创建字符串

const char * pt1 = "Something is pointing at me.";
该声明和下面的声明几乎相同:
const char ar1[] = "Something is pointing at me.";

pt1和ar1都是该字符串的地址。在这两种情况下,带双引号的字符串本身决定了预留给字符串的存储空间。尽管如此,这两种形式并不完全相同。

3.数组和指针

在数组形式中,ar1是地址常量。不能更改ar1,如果改变了ar1,则意味着改变了数组的存储位置(即地址)。可以进行类似ar1+1这样的操作,标识数组的下一个元素。但是不允许进行++ar1这样的操作。递增运算符只能用于变量名前(或概括地说,只能用于可修改的左值),不能用于常量。

该变量最初指向该字符串的首字符,但是它的值可以改变。因此,可以使用递增运算符。例如,++pt1将指向第 2 个字符(o)。

字符串字面量被视为const数据。由于pt1指向这个const数据,所以应该把pt1声明为指向const数据的指针。这意味着不能用pt1改变它所指向的数据,但是仍然可以改变pt1的值(即,pt1指向的位置)。如果把一个字符串字面量拷贝给一个数组,就可以随意改变数据,除非把数组声明为const。

总之,初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针。

4.数组和指针的区别

char heart[] = "I love Tillie!";
const char *head = "I love Millie!";

两者主要的区别是:数组名heart是常量,而指针名head是变量。

首先,两者都可以使用数组表示法

其次,两者都能进行指针加法操作:

但是,只有指针表示法可以进行递增操作:

假设想让head和heart统一,可以这样做:
head = heart;   /* head现在指向数组heart */
这使得head指针指向heart数组的首元素。
但是,不能这样做:
heart = head;   /* 非法构造,不能这样写 */
这类似于x = 3;和3 = x;的情况。赋值运算符的左侧必须是变量(或概括地说是可修改的左值)

另外,还可以改变heart数组中元素的信息:

数组的元素是变量(除非数组被声明为const),但是数组名不是变量

建议在把指针初始化为字符串字面量时使用const限定符

总之,如果不修改字符串,不要用指针指向字符串字面量。

5.字符串数组

图11.2 矩形数组和不规则数组

综上所述,如果要用数组表示一系列待显示的字符串,请使用指针数组,因为它比二维字符数组的效率高。但是,指针数组也有自身的缺点。mytalents 中的指针指向的字符串字面量不能更改;而yourtalentsde 中的内容可以更改。所以,如果要改变字符串或为字符串输入预留空间,不要使用指向字符串字面量的指针

11.1.2 指针和字符串

显示两个指针的值。所谓指针的值就是它储存的地址。mesg 和 copy 的值都是0x0040a000,说明它们都指向的同一个位置。因此,程序并未拷贝字符串。语句copy = mesg;把mesg的值赋给copy,即让copy也指向mesg指向的字符串

11.2 字符串输入

如果想把一个字符串读入程序,首先必须预留储存该字符串的空间,然后用输入函数获取该字符串。

11.2.1 分配空间

最简单的方法是,在声明时显式指明数组的大小:
char name[81];

11.2.2 不幸的gets()函数

gets()函数简单易用,它读取整行输入,直至遇到换行符,然后丢弃换行符,储存其余字符,并在这些字符的末尾添加一个空字符使其成为一个 C 字符串。它经常和 puts()函数配对使用,该函数用于显示字符串,并在末尾添加换行符。程序清单11.6中演示了这两个函数的用法。

问题出在 gets()唯一的参数是 words,它无法检查数组是否装得下输入行

如果输入的字符串过长,会导致缓冲区溢出(buffer overflow),即多余的字符超出了指定的目标空间。

11.2.3 gets()的替代品

1.fgets()函数(和fputs())

fgets()函数的第2个参数指明了读入字符的最大数量。如果该参数的值是n,那么fgets()将读入n-1个字符,或者读到遇到的第一个换行符为止。
如果fgets()读到一个换行符,会把它储存在字符串中。这点与gets()不同,gets()会丢弃换行符。
fgets()函数的第3 个参数指明要读入的文件。如果读入从键盘输入的数据,则以stdin(标准输入)作为参数,该标识符定义在stdio.h中。

因为 fgets()函数把换行符放在字符串的末尾(假设输入行不溢出),通常要与 fputs()函数(和puts()类似)配对使用,除非该函数不在字符串末尾添加换行符。fputs()函数的第2个参数指明它要写入的文件。如果要显示在计算机显示器上,应使用stdout(标准输出)作为该参数。程序清单11.7演示了fgets()和fputs()函数的用法。

fputs()不在字符串末尾添加换行符

fputs()函数返回指向 char的指针。如果一切进行顺利,该函数返回的地址与传入的第 1 个参数相同。但是,如果函数读到文件结尾,它将返回一个特殊的指针:空指针(null pointer)。该指针保证不会指向有效的数据,所以可用于标识这种特殊情况。在代码中,可以用数字0来代替,不过在C语言中用宏NULL来代替更常见(如果在读入数据时出现某些错误,该函数也返回NULL)。

首先,如何处理掉换行符?一个方法是在已储存的字符串中查找换行符,并将其替换成空字符:
while (words[i] != '\n') // 假设\n在words中
i++;
words[i] = '\0';
其次,如果仍有字符串留在输入行怎么办?一个可行的办法是,如果目标数组装不下一整行输入,就丢弃那些多出的字符:
while (getchar() != '\n') // 读取但不储存输入,包括\n
continue;

空字符(或'\0')是用于标记C字符串末尾的字符,其对应字符编码是0。由于其他字符的编码不可能是 0,所以不可能是字符串的一部分。

空指针(或NULL)有一个值,该值不会与任何数据的有效地址对应。通常,函数使用它返回一个有效地址表示某些特殊情况发生,例如遇到文件结尾或未能按预期执行。

空字符是整数类型,而空指针是指针类型。两者有时容易混淆的原因是:它们都可以用数值0来表示。但是,从概念上看,两者是不同类型的0。另外,空字符是一个字符,占1字节;而空指针是一个地址,通常占4字节。

2.gets_s()函数

gets_s()只从标准输入中读取数据,所以不需要第3个参数。

如果gets_s()读到换行符,会丢弃它而不是储存它。

如果gets_s()读到最大字符数都没有读到换行符,会执行以下几步。首先把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直至读到换行符或文件结尾,然后返回空指针。接着,调用依赖实现的“处理函数”(或你选择的其他函数),可能会中止或退出程序。

当输入与预期不符时,gets_s()完全没有fgets()函数方便、灵活。也许这也是gets_s()只作为C库的可选扩展的原因之一。鉴于此,fgets()通常是处理类似情况的最佳选择。

3.s_gets()函数

读取整行输入并用空字符代替换行符,或者读取一部分输入,并丢弃其余部分。既然没有处理这种情况的标准函数,我们就创建一个,在后面的程序中会用得上。

丢弃输入行余下的字符保证了读取语句与键盘输入同步。

11.2.4 scanf()函数

scanf()更像是“获取单词”函数,而不是“获取字符串”函数

无论哪种方法,都从第1个非空白字符作为字符串的开始。如果使用%s转换说明,以下一个空白字符(空行、空格、制表符或换行符)作为字符串的结束(字符串不包括空白字符)。如果指定了字段宽度,如%10s,那么scanf()将读取10 个字符或读到第1个空白字符停止(先满足的条件即是结束输入的条件)

图11.3 字段宽度和scanf()

scanf()函数返回一个整数值,该值等于scanf()成功读取的项数或EOF(读到文件结尾时返回EOF)。

根据输入数据的性质,用fgets()读取从键盘输入的数据更合适。例如,scanf()无法完整读取书名或歌曲名,除非这些名称是一个单词。scanf()的典型用法是读取并转换混合数据类型为某种标准形式。例如,如果输入行包含一种工具名、库存量和单价,就可以使用scanf()。否则可能要自己拼凑一个函数处理一些输入检查。如果一次只输入一个单词,用scanf()也没问题。

在%s转换说明中使用字段宽度可防止溢出

11.3 字符串输出

11.3.1 puts()函数

puts()函数很容易使用,只需把字符串的地址作为参数传递给它即可。

puts()在显示字符串时会自动在其末尾添加一个换行符。

用双引号括起来的内容是字符串常量,且被视为该字符串的地址。另外,储存字符串的数组名也被看作是地址。在第5个puts()调用中,表达式&str1[5]是str1数组的第6个元素(r),puts()从该元素开始输出。与此类似,第6个puts()调用中,str2+4指向储存"pointer"中i的存储单元,puts()从这里开始输出。

puts()如何知道在何处停止?该函数在遇到空字符时就停止输出,所以必须确保有空字符。

由于dont缺少一个表示结束的空字符,所以它不是一个字符串,因此puts()不知道在何处停止。它会一直打印dont后面内存中的内容,直到发现一个空字符为止。

11.3.2 fputs()函数

fputs()函数是puts()针对文件定制的版本。

fputs()函数的第 2 个参数指明要写入数据的文件。如果要打印在显示器上,可以用定义在stdio.h中的stdout(标准输出)作为该参数。

与puts()不同,fputs()不会在输出的末尾添加换行符。

line数组中的字符串显示在下一行

line数组中的字符串也显示在下一行

如果混合使用 fgets()输入和puts()输出,每个待显示的字符串末尾就会有两个换行符。这里关键要注意:puts()应与gets()配对使用,fputs()应与fgets()配对使用。

11.3.3 printf()函数

printf()也把字符串的地址作为参数

printf()不会自动在每个字符串末尾加上一个换行符。因此,必须在参数中指明应该在哪里使用换行符。例如:
printf("%s\n", string);
和下面的语句效果相同:
puts(string);

printf()的形式更复杂些,需要输入更多代码,而且计算机执行的时间也更长(但是你觉察不到)。

使用 printf()打印多个字符串更加简单。

11.4 自定义输入/输出函数

完全可以在getchar()和putchar()的基础上自定义所需的函数。

/* put1.c -- 打印字符串,不添加\n */
#include <stdio.h>
void put1(const char * string)/* 不会改变字符串 */
{
while (*string != '\0')
putchar(*string++);
}

用数组表示法编写这个函数稍微复杂些:
int i = 0;
while (string[i]!= '\0')
putchar(string[i++]);
要为数组索引创建一个额外的变量。

许多C程序员会在while循环中使用下面的测试条件:
while (*string)

使用带方括号的写法是为了提醒用户:该函数处理的是数组。

用const char * string可以提醒用户:实际参数不一定是数组。

11.5 字符串函数

ANSI C把这些函数的原型放在string.h头文件中。其中最常用的函数有 strlen()、strcat()、strcmp()、strncmp()、strcpy()和 strncpy() 还有sprintf()函数,其原型在stdio.h头文件中

11.5.1 strlen()函数

fit()函数把第39个元素的逗号替换成'\0'字符。puts()函数在空字符处停止输出,并忽略其余字符。然而,这些字符还在缓冲区中,下面的函数调用把这些字符打印了出来

所以put()显示该字符并继续输出直至遇到原来字符串中的空字符

11.5.2 strcat()函数

strcat()(用于拼接字符串)函数接受两个字符串作为参数。该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后形成的新字符串作为第1个字符串,第2个字符串不变。strcat()函数的类型是char *(即,指向char的指针)。strcat()函数返回第1个参数,即拼接第2个字符串后的第1个字符串的地址。

11.5.3 strncat()函数

strcat()函数无法检查第1个数组是否能容纳第2个字符串

用strlen()查看第1个数组的长度

用strncat(),该函数的第3 个参数指定了最大添加字符数。例如,strncat(bugs, addon, 13)将把 addon字符串的内容附加给bugs,在加到第13个字符或遇到空字符时停止

11.5.4 strcmp()函数

由于非零值都为“真”,所以许多经验丰富的C程序员会把该例main()中的while循环头写成:while (strcmp(try, ANSWER))

strcmp()函数只会比较try中第1个空字符前面的部分。所以,可以用strcmp()比较储存在不同大小数组中的字符串

ASCII标准规定,在字母表中,如果第1个字符串在第2个字符串前面,strcmp()返回一个负数;如果两个字符串相同,strcmp()返回0;如果第1个字符串在第2个字符串后面,strcmp()返回正数

如果两个字符串开始的几个字符都相同会怎样?一般而言,strcmp()会依次比较每个字符,直到发现第 1 对不同的字符为止

char 类型实际上是整数类型,所以可以使用关系运算符来比较字符

strcmp()函数比较字符串中的字符,直到发现不同的字符为止,这一过程可能会持续到字符串的末尾。而strncmp()函数在比较两个字符串时,可以比较到字符不同的地方,也可以只比较第3个参数指定的字符数

11.5.5 strcpy()和strncpy()函数

如果希望拷贝整个字符串,要使用strcpy()函数

strcpy()函数相当于字符串赋值运算符

strcpy()第2个参数(temp)指向的字符串被拷贝至第1个参数(qword[i])指向的数组中。拷贝出来的字符串被称为目标字符串,最初的字符串被称为源字符串

target = "So long";    /* 语法错误 */

strcpy()接受两个字符串指针作为参数,可以把指向源字符串的第2个指针声明为指针、数组名或字符串常量;而指向源字符串副本的第1个指针应指向一个数据对象(如,数组),且该对象有足够的空间储存源字符串的副本。记住,声明数组将分配储存数据的空间,而声明指针只分配储存一个地址的空间

第一,strcpy()的返回类型是 char *,该函数返回的是第 1个参数的值,即一个字符的地址。第二,第 1 个参数不必指向数组的开始。这个属性可用于拷贝数组的一部分

strcpy()把源字符串中的空字符也拷贝在内

strcpy()和 strcat()都有同样的问题,它们都不能检查目标空间是否能容纳源字符串的副本。拷贝字符串用 strncpy()更安全,该函数的第 3 个参数指明可拷贝的最大字符数

strncpy(target, source, n)把source中的n个字符或空字符之前的字符(先满足哪个条件就拷贝到何处)拷贝至target中

这样做确保储存的是一个字符串。如果目标空间能容纳源字符串的副本,那么从源字符串拷贝的空字符便是该副本的结尾;如果目标空间装不下副本,则把副本最后一个元素设置为空字符。

11.5.6 sprintf()函数

sprintf()函数声明在stdio.h中,而不是在string.h中

sprintf()的第1个参数是目标字符串的地址。其余参数和printf()相同,即格式字符串和待写入项的列表

sprintf()的用法和printf()相同,只不过sprintf()把组合后的字符串储存在数组formal中而不是显示在屏幕上

11.5.7 其他字符串函数

ANSI C库有20多个用于处理字符串的函数,下面总结了一些常用的函数

11.6 字符串示例:字符串排序

11.6.1 排序指针而非字符串

11.6.2 选择排序算法

选择排序算法(selection sort algorithm)
利用for循环依次把每个元素与首元素比较。如果待比较的元素在当前首元素的前面,则交换两者。循环结束时,首元素包含的指针指向机器排序序列最靠前的字符串。然后外层for循环重复这一过程,这次从input的第2个元素开始。当内层循环执行完毕时,ptrst中的第2个元素指向排在第2的字符串。这一过程持续到所有元素都已排序完毕

11.7 ctype.h字符函数和字符串

第7章中介绍了ctype.h系列与字符相关的函数。虽然这些函数不能处理整个字符串,但是可以处理字符串中的字符

11.8 命令行参数

C编译器允许main()没有参数或者有两个参数(一些实现允许main()有更多参数,属于对标准的扩展)。main()有两个参数时,第1个参数是命令行中的字符串数量。过去,这个int类型的参数被称为argc (表示参数计数(argument count))。系统用空格表示一个字符串的结束和下一个字符串的开始。因此,上面的repeat示例中包括命令名共有4个字符串,其中后3个供repeat使用。该程序把命令行字符串储存在内存中,并把每个字符串的地址储存在指针数组中。而该数组的地址则被储存在 main()的第 2 个参数中。按照惯例,这个指向指针的指针称为argv(表示参数值[argument value])。

11.8.1 集成环境中的命令行参数

11.8.2 Macintosh中的命令行参数

11.9 把字符串转换为数字

数字既能以字符串形式储存,也能以数值形式储存。把数字储存为字符串就是储存数字字符。例如,数字213以'2'、'1'、'3'、'\0'的形式被储存在字符串数组中。以数值形式储存213,储存的是int类型的值

C要求用数值形式进行数值运算(如,加法和比较)。但是在屏幕上显示数字则要求字符串形式,因为屏幕显示的是字符

如果需要整数,可以使用atoi()函数(用于把字母数字转换成整数),该函数接受一个字符串作为参数,返回相应的整数值

如果字符串仅以整数开头,atio()函数也能处理,它只把开头的整数转换为字符。例如, atoi("42regular")将返回整数42

在我们所用的C实现中,如果命令行参数不是数字,atoi()函数返回0。然而C标准规定,这种情况下的行为是未定义的。因此,使用有错误检测功能的strtol()函数(马上介绍)会更安全

stdlib.h头文件,因为从ANSI C开始,该头文件中包含了atoi()函数的原型。除此之外,还包含了 atof()和 atol()函数的原型。atof()函数把字符串转换成 double 类型的值, atol()函数把字符串转换成long类型的值。atof()和atol()的工作原理和atoi()类似,因此它们分别返回double类型和long类型

strtol()把字符串转换成long类型的值,strtoul()把字符串转换成unsigned long类型的值,strtod()把字符串转换成double类型的值。这些函数的智能之处在于识别和报告字符串中的首字符是否是数字。而且,strtol()和strtoul()还可以指定数字的进制

long strtol(const char * restrict nptr, char ** restrict endptr, int base);
这里,nptr是指向待转换字符串的指针,endptr是一个指针的地址,该指针被设置为标识输入数字结束字符的地址,base表示以什么进制写入数字

首先注意,当base分别为10和16时,字符串"10"分别被转换成数字10和16。还要注意,如果end指向一个字符,*end就是一个字符。因此,第1次转换在读到空字符时结束,此时end指向空字符。打印end会显示一个空字符串,以%d转换说明输出*end显示的是空字符的ASCII码

对于第2个输入的字符串,当base为10时,end的值是'a'字符的地址。所以打印end显示的是字符串"atom",打印*end显示的是'a'字符的ASCII码。然而,当base为16时,'a'字符被识别为一个有效的十六进制数,strtol()函数把十六进制数10a转换成十进制数266

strtol()函数最多可以转换三十六进制,'a'~'z'字符都可用作数字。strtoul()函数与该函数类似,但是它把字符串转换成无符号值。strtod()函数只以十进制转换,因此它值需要两个参数

许多实现使用 itoa()和 ftoa()函数分别把整数和浮点数转换成字符串。但是这两个函数并不是 C标准库的成员,可以用sprintf()函数代替它们,因为sprintf()的兼容性更好

11.10 关键概念

11.11 本章小结

C字符串是一系列char类型的字符,以空字符('\0')结尾。字符串可以储存在字符数组中。字符串还可以用字符串常量来表示,里面都是字符,括在双引号中(空字符除外)。编译器提供空字符。因此,"joy"被储存为4个字符j、o、y和\0。strlen()函数可以统计字符串的长度,空字符不计算在内。
字符串常量也叫作字符串——字面量,可用于初始化字符数组。为了容纳末尾的空字符,数组大小应该至少比容纳的数组长度多1。也可以用字符串常量初始化指向char的指针。
函数使用指向字符串首字符的指针来表示待处理的字符串。通常,对应的实际参数是数组名、指针变量或用双引号括起来的字符串。无论是哪种情况,传递的都是首字符的地址。一般而言,没必要传递字符串的长度,因为函数可以通过末尾的空字符确定字符串的结束。
fgets()函数获取一行输入,puts()和 fputs()函数显示一行输出。它们都是 stdio.h 头文件中的函数,用于代替已被弃用的gets()。
C库中有多个字符串处理函数。在ANSI C中,这些函数都声明在string.h文件中。C库中还有许多字符处理函数,声明在ctype.h文件中。
给main()函数提供两个合适的形式参数,可以让程序访问命令行参数。第1个参数通常是int类型的argc,其值是命令行的单词数量。第2个参数通常是一个指向数组的指针argv,数组内含指向char的指针。每个指向char的指针都指向一个命令行参数字符串,argv[0]指向命令名称,argv[1]指向第1个命令行参数,以此类推。
atoi()、atol()和atof()函数把字符串形式的数字分别转换成int、long 和double类型的数字。strtol()、strtoul()和strtod()函数把字符串形式的数字分别转换成long、unsigned long和double类型的数字。
 

相关文章:

第11章 字符串和字符串函数

本章介绍以下内容&#xff1a; 函数&#xff1a;gets()、gets_s()、fgets()、puts()、fputs()、strcat()、strncat()、strcmp()、strncmp()、strcpy()、strncpy()、sprintf()、strchr() 创建并使用字符串 使用C库中的字符和字符串函数&#xff0c;并创建自定义的字符串函数 使用…...

TypeScript项目配置

前言 我们需要建立tsconfig.json 作用 用于标识 TypeScript 项目的根路径&#xff1b; 用于配置 TypeScript 编译器&#xff1b; 用于指定编译的文件。 重要字段 files - 设置要编译的文件的名称&#xff1b; include - 设置需要进行编译的文件&#xff0c;支持…...

【Spring面试】二、BeanFactory与IoC容器的加载

文章目录 Q1、BeanFactory的作用是什么&#xff1f;Q2、BeanDefinition的作用是什么&#xff1f;Q3、BeanFactory和ApplicationContext有什么区别&#xff1f;Q4、BeanFactory和FactoryBean有什么区别&#xff1f;Q5、说下Spring IoC容器的加载过程&#xff08;※&#xff09;Q…...

Android嵌套事务

这时候旋转设备还是会重置秒表。旋转设备时Android会重新创建活动。如果你的活动包含一个 < fragment >元素&#xff0c;每次重新创建活动时&#xff0c;它会重新插入片段的一个新版本。老片段被丢掉&#xff0c;所有实例变量会设置其初始值。在这个特定的例子中&#xf…...

如何让项目准时上线?

项目为什么容易延期&#xff1f; 1、软件研发是一项创造性工作 项目延期是一种普遍现象&#xff0c;管理者最为头疼的一个问题。但是外人并不理解。明明是你们自己做的计划&#xff0c;怎么总会出现这么多问题。说到底&#xff0c;这是由于我们的工作特性决定的。我们做的是一…...

ChatGPT 和 Elasticsearch:APM 工具、性能和成本分析

作者&#xff1a;LUCA WINTERGERST 在本博客中&#xff0c;我们将测试一个使用 OpenAI 的 Python 应用程序并分析其性能以及运行该应用程序的成本。 使用从应用程序收集的数据&#xff0c;我们还将展示如何将 LLMs 成到你的应用程序中。 在之前的博客文章中&#xff0c;我们构建…...

不使用辅助变量的前提下实现两个变量的交换

package operator; //不用第三个辅助变量&#xff0c;实现两个数的交换 public class Demo08 {public static void change(int a, int b){a ab;b a-b;a a-b;System.out.println(a);System.out.println(b);}public static void main(String[] args) {change(900,3000);} }后续…...

SV-DJS-i13电梯对讲网关

SV-DJS-i13电梯对讲网关 DJS-I13 是一款主要应用于电梯场景的对讲设备&#xff0c;可以将电梯原有模拟通话器的模拟信号转换成数字信号&#xff0c;不仅有稳定性好、电信级音质的优点&#xff0c;且完美兼容当下所有基于SIP的主流IPPBX/软交换/IMS平台,如Asterisk, Broadsoft,…...

论文解析-基因序列编码算法DeepSEA

论文解析-DeepSEA 参考亮点功能 方法数据集来源数据 实验评估评估DeepSEA预测染色质特征的性能评估DeepSEA在变异序列上的DHS预测性能数据集结果 参考 Zhou, J., Troyanskaya, O. Predicting effects of noncoding variants with deep learning–based sequence model. Nat Me…...

计组与操作系统

非科班出身的程序员&#xff0c;还是得补一下相关理论课程&#xff0c;最近看了下九曲阑干关于CSAPP的视频&#xff0c;学习了一下计算机组成原理&#xff0c;这里列一下相关知识点。 计算机组成原理&#xff1a; 数的表示与运算&#xff1a;CSAPP第二章 指令系统&#xff0…...

Pytorch中张量矩阵乘法函数(mm, bmm, matmul)使用说明,含高维张量实例及运行结果

Pytorch中张量矩阵乘法函数使用说明 1 torch.mm() 函数1.1 torch.mm() 函数定义及参数1.2 torch.bmm() 官方示例 2 torch.bmm() 函数2.1 torch.bmm() 函数定义及参数2.2 torch.bmm() 官方示例 3 torch.matmul() 函数3.1 torch.matmul() 函数定义及参数3.2 torch.matmul() 规则约…...

如何在matlab绘图的标题中添加变量?变量的格式化字符串输出浅析

文章目录 matlab的格式化输出控制符字段宽度、精度和对齐方式的控制matlab的格式化输出总结 matlab的格式化输出控制符 Matlab在画图的时候&#xff0c;采用title函数可以增加标题&#xff0c;该函数的输入是一个字符串&#xff0c;有时候我们想在字符串中添加一些变量&#x…...

Spring MVC 八 - 内置过滤器

SpringMVC内置如下过滤器&#xff1a; Form DataForwarded HeadersShallow ETagCORS Form Data 浏览器可以通过HTTP GET或HTTP POST提交form data&#xff08;表单数据&#xff09;&#xff0c;但是非浏览器客户端可以通过HTTP PUT、HTTP DELETE、HTTP PATCH提交表单数据。但…...

@Change监听事件与vue监听属性:watch的区别?

change 和 watch 是 Vue 中用于处理数据变化的两种不同方式。 1. change: - change 是一个事件监听器&#xff0c;用于监听特定DOM元素的变化事件&#xff0c;通常用于表单元素&#xff08;如输入框、下拉框等&#xff09;的值变化。 - 它在用户与表单元素交互并提交了变化时触…...

C++面试记录之中望软件

上次面试体验不好&#xff0c;记录了&#xff0c;这次同样记录一次体验不好的面试&#xff0c;中望软件…直接写了名字&#xff0c;因为真的很无语&#x1f613; 记录一下我不知道的问题 忘记录音了&#x1f622; 1. main函数之前做了什么&#xff1f; 我&#xff1a;实话我…...

多功能翻译工具:全球翻译、润色和摘要生成 | 开源日报 0914

openai-translator/openai-translator Stars: 18.1k License: AGPL-3.0 这个项目是一个多功能翻译工具&#xff0c;由 OpenAI 提供支持。 可以进行全球单词翻译、单词润色和摘要生成等操作提供三种模式&#xff1a;翻译、润色和摘要支持 55 种不同语言的互相转换支持流模式允…...

在 Vue.js 中,使用 watch 监听data变量如:对象属性/data变量

watch 监听对象属性 在 Vue.js 中&#xff0c;使用 watch 监听对象属性的变化时&#xff0c;应该将属性名作为字符串传递给 watch 选项。 示例如下&#xff1a; javascript watch: {addform.isCheck1: function(newValue) {console.log(newValue);var quantity this.addform…...

vue中预览xml并高亮显示

项目中有需要将接口返回的数据流显示出来&#xff0c;并高亮显示&#xff1b; 1.后端接口返回blob,类型为xml,如图 2.页面中使用pre code标签&#xff1a; <pre v-if"showXML"><code class"language-xml">{{xml}}</code></pre> …...

MFC中嵌入显示opencv窗口

在MFC窗体中建立一个Picture Control控件,用于显示opencv窗口 在属性中设置图片控件的资源ID为IDC_PIC1 主要的思路: 使用GetWindowRect可以获取图片控件的区域 使用cv::resizeWindow可以设置opencv窗口的大小,适合图片控件的大小 使用cvGetWindowHandle函数可以获取到ope…...

金鸣识别网页版:轻松实现表格识别的神器

来百度APP畅享高清图片 金鸣识别网页版是一款功能强大的在线识别工具&#xff0c;它可对图片或PDF中的表格文本内容进行识别&#xff0c;还支持各种证票的结构化识别。以下是以表格识别为例&#xff0c;对金鸣识别网页版的操作说明进行详细介绍&#xff1a; 首先&#xff0c;打…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...