2019-04-21 C正则,sprintf,sccanf

                                        C正则

简介
第一部分:C/C++如何使用正则;
第二部分:正则介绍
第三部分:C中使用部分正则的函数(sccanf,sprintf等)

                        第一部分:C/C++如何使用正则

一.介绍:
核心: 标准的C和C++都不支持正则表达式,但函数库提供该功能:函数如下:

1)编译正则表达式 regcomp()
2)匹配正则表达式 regexec()
3)释放正则表达式 regfree()
4)获取regcomp 或者regexec 产生错误,获取包含错误信息的字符串
  1. 函数声明
    int regcomp (regex_t *compiled, const char *pattern, int cflags)
参数1: regex_t *compiled—》一种特定的数据格式。
参数2: const char *pattern—》指定的正则表达式
参数3: 有如下4个值或者是它们或运算(|)后的值:
   REG_EXTENDED 以功能更加强大的扩展正则表达式的方式进行匹配。
   REG_ICASE    匹配字母时忽略大小写。
   REG_NOSUB  不用存储匹配后的结果。
   REG_NEWLINE 识别换行符,'$'从行尾开始匹配,'^'从行开头开始匹配。

作用: 将正则表达式编译成regex_t结构体格式。下面函数会处理编译的正则表达式。
返回值:成功返回0.

  1. int regexec (regex_t *compiled, char *string, size_t nmatch, regmatch_t matchptr [], int eflags)
    作用: 使用编译好的正则表达式,进行匹配字符串—》找到想要的字符串
参数1: compiled 是用regcomp函数编译好的正则表达式。
参数2: string 是目标文本串。
参数3: nmatch 是regmatch_t结构体数组的长度。
参数4: matchptr regmatch_t类型的结构体数组,存放匹配文本串的位置信息。
         即:用于把匹配结果返回给调用程序。
参数5:eflags 有两个值
取值1: REG_NOTBOL不匹配行的开头,除非在 regcomp 编译时 cflag 设置 REG_NEWLINE。
         '^'匹配行的开头 , 不管 regexec 中是否设置 eflags 为 REG_NOTBOL 。
  取值2: REG_NOTEOL不匹配行的结束,除非在 regcomp 编译时 cflag 设置 
         REG_NEWLINE 。'$' 匹配行的末尾 , 不管 regexec 中是否设置 eflags 为
      REG_NOTEOL 。
  返回值:执行成功返回0。
  1. void regfree (regex_t *compiled)
    作用: 清空compiled指向regex_t结构体内容。如重新编译,一定先清空regex_t结构体。
  2. size_t regerror (int errcode, regex_t *compiled, char *buffer, size_t length)
    作用:执行前两个函数产生错误的时候,就可调用该函数而返回包含错误信息的字符串。
参数1: errcode 是由regcomp 和 regexec 函数返回的错误代号。
参数2: compiled 是已经用regcomp函数编译好的正则表达式,这个值可以为NULL。
参数3:buffer 指向用来存放错误信息的字符串的内存空间。如果不需要,可为空
参数4: length 指明buffer的长度。如果该值小于错误信息长度:
       函数会自动截断超出的字符串,但他仍然会返回完整的字符串的长度。
size_t length = regerror (errcode, compiled, NULL, 0);

添加头文件
#include <sys/types.h>
#include <regex.h>

                               第二部分:正则介绍

一.用于字符串操作
定义: 用事先定义好的特定字符组合,进行“字符串匹配”的规律逻辑 。
组成: 普通字符+ 元字符(特殊字符,限定符,定位符)

     特殊字符—》特殊含义的字符
     限定符------》限定出现的次数;
     定位符-----》限定出现的位置;

1. 特殊字符

(1)$   匹配输入字符串的结尾位置。要匹配 $ 字符本身,请使用 \$。
(2)( )  标记一个子表达式的开始和结束位置。
(3)*    匹配前面的子表达式零次或多次。    要匹配 * 字符请使用 \*。
(4)+    匹配前面的子表达式一次或多次。    要匹配 + 字符请使用 \+。
(5)?    匹配前面的子表达式零次或一次。要匹配 ? 字符请使用 \?。
(6).    匹配除换行符 \n 之外的任何单字符。要匹配 . 请使用 \. 。
(7)[    标记一个中括号表达式的开始。  要匹配 [请使用 \[。
 集合介绍: 
     [xyz]  匹配所包含的任意一个字符。
     [^xyz]  匹配未包含的任意字符。
     [a-z]   字符范围。匹配指定范围内的任意字符。
     [^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。
(8)\    转移字符,如:\? 就匹配到?
(9)^  (方括号外[])匹配输入字符串的开始位置;
   (方括号内[])表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
(10){   标记限定符表达式的开始。要匹配 {请使用 \{。
(11)|   指明两项之间的一个选择。要匹配 |请使用 \|。
    例子:x|y  匹配 x 或 y。

2. 限定符(限定次数)有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m}

*   匹配前面的子表达式零次或多次。 * 等价于{0,}
+   匹配前面的子表达式一次或多次。 + 等价于 {1,}
?   匹配前面的子表达式零次或一次。 ? 等价于 {0,1}
{n} 匹配确定的 n 次。
{n,}    至少匹配n 次。
{n,m}    其中n <= m。最少匹配 n 次且最多匹配 m 次。
例子:ZO* 可以匹配到Z,ZO,ZOO…,因为可以0次,即:不要前面的单个字符

3. 定位符(描述字符串或单词的边界)

  ^ 和 $ 分别指字符串的开始与结束,
  \b 描述单词的前或后边界,
  \B 表示非单词边界

4. 正则的优先级

2.png

5. 例子简介

(1)^[a-z][0-9]$     #重点是开头,结束定位符的使用。
    首先:[]优先级最高,位置限定^$较低。
    即:以一个字母开头 加一个数字结束。
    注意:没有限定符,即一次,可以是s5,但不可以是s55。
(2)^[^0-9][0-9]$    #重点是[]内的^ 与 []外的^区别
      首先:[]内的^ 是“排除”的意思,不是数字。加上[]外的^ :不以数字开头;
      然后:以数字结束的两位字符。
(3)[^\\\/\^]  注意: \ 是转义字符, \\, \/, \^
       即:除了(\)(/)(^)之外的所有字符
                  部分三.  C支持正则的函数(部分支持)

一.sscanf

#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);    #文件输入
int sscanf(const char *str, const char *format, ...);(用于拆分字符串)

相同点:都是可变参数,都是输入函数—》按照一定形式存入到…部分。
不同点:输入原不同—》stdin(标准输入)输入;文件输入;字符串输入;

1. sscanf函数(从字符串提取数据):用于字符串操作!!

* 根据格式从字符串中提取数据。
* 取指定长度的字符串
* 取到指定字符为止的字符串
* 取仅包含指定字符集的字符串
* 取到指定字符集为止的字符串

(1)功能1: sscanf支持格式字符%[]

  -    表示范围,如:%[1-9]表示只读取1-9这几个数字 
       %[a-z]表示只读取a-z小写字 注:遇到不是的停止。
     char s[]="hello, my friend”;   // 注意, 逗号在不 a-z 之间
     sscanf( s, “%[a-z]”, string ) ;   // string=hello
  ^   表示不取,如:%[^1]表示读取除'1'以外的所有字符
       %[^/]表示除/以外的字符
     例子: 如果碰到 a-z 之间的字符则停止
         char s[]="HELLOkitty” ; 
         sscanf( s, “%[^a-z]”, string ) ; // string=HELLO
  ,   范围可以用","相连接 如%[1-9,a-z]表示同时取1-9数字和a-z小写字母 
 *    表示不保存变量, 跳过此数据不读入. %*[^=] , %*s跳过符合条件的字符串。
       char s[]="notepad=1.0.0.1001" ;
       int i = sscanf( s, "%*[^=]", string ) ;   #为NULL, 因为没保存
       int i = sscanf( s, "%*[^=]=%s",string) ; 
            #为1.0.0.1001等号前不取,取等号后

(2)功能2 取指定长度的字符串—》取最大长度为4字节的字符串。

 sscanf("123456 ", "%4s", buf);
 printf("%s\n", buf);   结果为:1234

(3)功能3: 取到指定字符为止的字符串---》取遇到空格为止字符串。

  sscanf("123456 abcdedf", "%[^ ]", buf);  # 遇到空格结束
  printf("%s\n", buf);   结果为:123456

(4)功能4: 取仅包含指定字符集的字符串---》取仅包含1到9和小写字母的字符串。

   sscanf("123456abcdedfBCDEF", "%[1-9a-z]", buf);
   printf("%s\n", buf);  结果为:123456abcdedf

注:遇到不在范围内的就结束
(5)功能5: 取到指定字符集为止的字符串---》取遇到大写字母为止的字符串。

sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf);
printf("%s\n", buf);  结果为:123456abcdedf

历程1:给定一个字符串iios/12DDWDFF@122,获取 / 和 @ 之间的字符串,

先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中
sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf);
printf("%s\n", buf);  结果为:12DDWDFF
注:%s的结束条件—》遇回车(\n)或空格结束。

2. scanf()从控制台输入
int a,b,c;
printf("输入 a, b, c\n");
scanf("%d,%d,%d", &a, &b, &c);

3. fscanf 从文件输入,存入可变参数中
fscanf按照一定格式从文本中获取内容--》遇到空格和换行时结束(fgets遇到空格不结束)

 FILE *fp;
 char a[10];  int b;   double c;
 fscanf(fp,"%s%d%lf",a,&b,&c)

二.man printf 格式化输出

3.png

1. 格式化输出
共同点:将format后参数…按照format形式输出;
不同点: 输出到地方不同,分别--》stdout,文件,字符串中

 int printf(const char *format,***)              #写入stdout
 int fprintf(FILE *steam,const char *format,***)  #写入文件 
 int sprintf(char *str,const char *format,***)    #写入到字符串(参数1)中。
  1. sprintf的使用场景 :
场景1:格式化数字字符串--》 将整数转换为字符串。 
场景2:连接字符串--》 组合新的字符串(将几个字符串合并连接)。
场景3: 控制浮点数打印格式
场景4: 打印地址信息
场景5: sprintf的返回值是本次调用打印到字符缓冲区中的字符数目

缺点: 可能出现问题内存溢出,第一个参数长度太短,导致出现内存溢出而程序崩溃。

改善: int snprintf(char *str, size_t n ,const char *format,*** )
           #写入n个字符到字符串中。
   函数说明:最多从源串中拷贝n-1个字符到字符缓冲区,然后在后面加一个 “\0” 。
   函数返回值:若成功返回欲写入的字符串长度,出错返回负值。
   注意点:返回值是欲写入的字符串长度,并不是实际写入缓冲区中的长度。

(1)输出格式

b 解释为整数并作为二进制输出 
c 解释为整数并作为字符表示输出(ASCII码) 
d 解释为整数并作为整数输出 
e 解释为浮点数输出 
f 解释为双精度并作为浮点数输出 
o 解释为整数并作为八进制数输出 
s 解释为字符串并为字符串输出 
u 解释为无符号整数输出 
g 解释自动选择合适的表示法 
p 解释为指针的值 --》地址吗
x 解释为整数并作为带有小写字母a-f的十六进制数输出 
X 解释为整数并作为带有大写字母A-F的十六进制数输出

1). 可以在%”和字母(样式)之间插进数字—》表示最大宽度。

  %3d   :表示输出3位整型数, 不够3位右对齐。 
  %9.2f :表示输出宽度9浮点数, 小数位2, 整数位6, 小数点一位, 不够右对齐。 
 %8s   :表示输出8个字符的字符串, 不够8个字符右对齐。 
  注: 如果用浮点数表示—》字符或整型量的输出格式,: 
          小数点后的数字代表最大宽度, 
          小数点前的数字代表最小宽度。 
例如: %6.9s 表示长度不小于6且不大于9字符串。若大于9, 则第9个字符后的内容将被删除。

2). 可以在”%”和字母之间加小写字母l, 表示输出的是长型数。

%ld:  表示输出long整数 
%lf :  表示输出double浮点数

3).在”%”和字母之间加入一个”-” 号可说明输出为左对齐, 否则为右对齐。

%-7d: 表示输出7位整数左对齐 

4). 一些特殊规定字符

  ①\n 换行   ②\f 清屏并换页   ③\r 回车   ④\t Tab符 

(2)sprintf()使用
用途1: 格式化数字字符串,代替 itoa()

sprintf(s, “%d”, 123);    # 产生”123”
可以指定宽度,不足的左边补空格:
sprintf(s, “%8d%8d”, 123, 4567); //产生:“ 123 4567”
也可以左对齐:
 sprintf(s, “%-8d%8d”, 123, 4567); //产生:“123 4567”

用途2:控制浮点数打印格式(即:可控制浮点数的位数)

格式: ”%m.nf”,其中m 表示打印的宽度,n 表示小数点后的位数。
sprintf(s, “%10.3f”, 3.1415626);   # 产生:” 3.142”
sprintf(s, “%.3f”, 3.1415626);     # 不指定总宽度,产生:”3.142”

用途3: 连接字符串(替代strcat),且更强大

char* who = “I”;
char* whom = “you”;
sprintf(s, “%s love %s.”, who, whom);  //要求:参数1的要足够大,不溢出
注:也可以前后都只取部分字符:
char a1[] = {‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’};
char a2[] = {‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’};
sprintf(s, “%.6s%.5s”, a1, a2);//产生:”ABCDEFHIJKL”
注: 指定长度的数字可以是动态的,采用”*”来占用宽度或精度的常数位置
sprintf(s, “%.*s%.*s”, 7, a1, 7, a2);

用途4: 打印地址信息

间接地址打印: 十进制”%u” ,16 进制%x
直接地址打印%p: sprintf(s, “%p”, &i);

用途5: 利用sprintf 的返回值

 返回值: 本次函数调用最终打印到字符缓冲区中的字符数目。
 所以:其返回值就是偏移量,可以用于计数等,即写入的总长度:
offset += sprintf(s + offset, “%d,”, rand() % 100);
s[offset - 1] = ‘\n’;//将最后的逗号换成回车
  1. fprintf()的使用---》用于文件操作
int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中
fp = fopen ("file.txt", "w+"); 
fprintf(fp, "%s %s %s %d", "We", "are", "in", 2014);
fclose(fp);
  1. 不定数量参数 的格式化输出
    int vsprintf(char *string, char *format, va_list param);
    例子如下:vsprintf 是sprintf 的一个变形,用于执行有不定数量参数的函数。
参数1: 用于保存结果的字符串缓冲区
参数2: 一个格式化字符串。
参数3: 是指向格式化参数队列的指针。va_list、va_start和va_end宏(STDARG.H)
char buffer[256];
int vsceshi(char *format, ...)
{
   va_list aptr;
   int ret;
   va_start(aptr, format);
   ret = vsprintf(buffer, format, aptr);  //将结构存储到 buffer中
   va_end(aptr);
   return(ret);
}
 调用如下:vsceshi("%d  %s", i, str); 
  其余:略
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容