WPF中的常用布局

 

一 写在开头
1.1 写在开头
微软是一家伟大的公司。评价一门技术的好坏得看具体的需求,没有哪门技术是面面俱到地好,应该抛弃对微软和微软的技术的偏见。

1.2 本文内容
本文主要内容为WPF中的常用布局,大部分内容转载至https://blog.csdn.net/woshisunjiale/article/details/54136323,代码片段可能有所不同。

二 WPF中的常用布局
因为项目需要,所以得学习WPF开发。WPF使软件界面和逻辑相分离,手写xaml进行程序UI的开发是件很惬意的事情。从这点来说WPF要比Qt和GTK+要好。当然了,如果其能跨平台甚至开源那就更好了。但是,商业有商业自身的规律。
2.1 Canvas布局
Canvas是一个类似于坐标系的面板,所有的元素通过设置坐标来决定其在坐标系中的位置。具体表现为使用Left、Top、Right、 Bottom附加属性在Canvas中定位控件。

 1 <Window x:Class="WPF_Layout.MainWindow"
 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4 Title="WPF-Layout" Height="350" Width="525">
 5 <Grid>
 6 <Canvas>
 7 <Button Canvas.Left="50" Canvas.Top="50" Content="Button 1"></Button>
 8 <Button Canvas.Right="50" Canvas.Top="50" Content="Button 2"></Button>
 9 <Button Canvas.Left="50" Canvas.Bottom="50" Content="Button 3"></Button>
10 <Button Canvas.Right="50" Canvas.Bottom="50" Content="Button 4"></Button>
11 </Canvas>
12 </Grid>
13 </Window>

注意:如果同时设置 Canvas.Left="50"Canvas.Right="50",则以Canvas.Left="50"为准。如果同时设置Canvas.Top="50" Canvas.Bottom="50",则以Canvas.Top ="50"为准。(别这么丧心病狂地同时写Left和Right,请遵循基本的编程逻辑)

2.2 StackPanel布局
StackPanel将控件按照行或列来顺序排列,但不会换行。通过设置面板的Orientation属性设置了两种排列方式:横排(Horizontal默认的)和竖排(Vertical),默认为竖排(Vertical)。

 1 <Window x:Class="WPF_Layout.MainWindow"
 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4 Title="WPF-Layout" Height="350" Width="525">
 5 <Grid>
 6 <StackPanel Name="stackpanel1" Orientation="Horizontal">
 7 <Button Content="Button1"></Button>
 8 <Button Content="Button2"></Button>
 9 <Button Content="Button3"></Button>
10 </StackPanel>
11 <StackPanel Name="stackpanel2" Orientation="Vertical">
12 <Button Content="Button4"></Button>
13 <Button Content="Button5"></Button>
14 <Button Content="Button6"></Button>
15 </StackPanel>
16 <StackPanel Name="stackpanel3" Orientation="Horizontal" FlowDirection="RightToLeft">
17 <Button Content="Button7"></Button>
18 <Button Content="Button8"></Button>
19 <Button Content="Button9"></Button>
20 </StackPanel>
21 </Grid>
22 </Window>

注意:Orientation="Horizontal"时,设置FlowDirection属性为RightToLeft,,则元素将从右向左排列。

2.3 DockPanel布局
DockPanel支持让元素简单地停靠在整个面板的某一条边上,然后拉伸元素以填满全部宽度或高度。它也支持让一个元素填充其他已停靠元素没有占用的剩余空间。

DockPanel有一个Dock附加属性,因此子元素用4个值来控制她们的停靠:Left、Top、Right、Bottom。Dock没有Fill值。作为替代,最后的子元素将加入一个DockPanel并填满所有剩余的空间,除非DockPanel的LastChildFill属性为false,它将朝某个方向停靠。

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <DockPanel>
 7             <Button Content="上" DockPanel.Dock="Left"></Button>
 8             <Button Content="下" DockPanel.Dock="Bottom"></Button>
 9             <Button Content="左" DockPanel.Dock="Left"></Button>
10             <Button Content="右" DockPanel.Dock="Right"></Button>
11         </DockPanel>
12     </Grid>
13 </Window>

设置LastChildFill属性为false

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <DockPanel LastChildFill="False">
 7             <Button Content="上" DockPanel.Dock="Left"></Button>
 8             <Button Content="下" DockPanel.Dock="Bottom"></Button>
 9             <Button Content="左" DockPanel.Dock="Left"></Button>
10             <Button Content="右" DockPanel.Dock="Right"></Button>
11         </DockPanel>
12     </Grid>
13 </Window>

2.4 WrapPanel布局
WrapPanel布局面板将各个控件按照一定方向罗列,当长度或高度不够时自动调整进行换行换列。

Orientation="Horizontal"时各控件从左至右罗列,当面板长度不够时,子控件就会自动换行,继续按照从左至右的顺序排列。

Orientation="Vertical"时各控件从上至下罗列,当面板高度不够时,子控件就会自动换列,继续按照从上至下的顺序排列。

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <WrapPanel Orientation="Horizontal">
 7             <Button Content="Button 150" Width="150"></Button>
 8             <Button Content="Button 200" Width="200"></Button>
 9             <Button Content="Button 150" Width="150"></Button>
10             <Button Content="Button 200" Width="200"></Button>
11             <Button Content="Button 150" Width="150"></Button>
12             <Button Content="Button 200" Width="200"></Button>
13             <Button Content="Button 150" Width="150"></Button>
14         </WrapPanel>
15     </Grid>
16 </Window>

2.5 Grid布局
Grid允许我们通过自定义行列来进行布局,这类似于表格.通过定义Grid的RowDifinitions和ColumnDifinitions来实现对于表格行和列的定义,元素根据附加属性Grid.Row和Grid.Column确定自己的位置。

1)Grid的列宽与行高可采用固定、自动、按比列三种方式定义
第一种,固定长度——值为一个确定的数字
第二种,自动长度——值为Auto,实际作用就是取实际控件所需的最小值
第三种,比例长度——*表示占用剩余的全部宽度;两行都是*,将平分剩余宽度;一个2*,一个*,则前者占剩余全部宽度的2/3,后者占1/3;依此类推

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <Grid.RowDefinitions>
 7             <RowDefinition Height="40"></RowDefinition>
 8             <RowDefinition Height="Auto"></RowDefinition>
 9             <RowDefinition Height="2*"></RowDefinition>
10             <RowDefinition Height="*"></RowDefinition>
11         </Grid.RowDefinitions>
12         <Button Grid.Row="0" Content="Button 1"></Button>
13         <Button Grid.Row="1" Content="Button 2"></Button>
14         <Button Grid.Row="2" Content="Button 3"></Button>
15         <Button Grid.Row="3" Content="Button 4"></Button>
16     </Grid>
17 </Window>

2) 合并行或列

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <Grid.RowDefinitions>
 7             <RowDefinition Height="40"></RowDefinition>
 8             <RowDefinition Height="Auto"></RowDefinition>
 9             <RowDefinition Height="2*"></RowDefinition>
10             <RowDefinition Height="*"></RowDefinition>
11         </Grid.RowDefinitions>
12         <Button Grid.Row="0" Content="Button 1"></Button>
13         <Button Grid.Row="1" Content="Button 2"></Button>
14         <Button Grid.Row="2" Grid.RowSpan="2" Content="Button 3"></Button>
15     </Grid>
16 </Window>

3)GridSplitter重新分布Grid控件的列间距或行间距。(类似于WinForm中SplitContainer)

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <Grid.RowDefinitions>
 7             <RowDefinition Height="*"></RowDefinition>
 8             <RowDefinition Height="3"></RowDefinition>
 9             <RowDefinition Height="*"></RowDefinition>
10         </Grid.RowDefinitions>
11         <Button Grid.Row="0" Content="Button"></Button>
12         <GridSplitter Grid.Row="1" HorizontalAlignment="Stretch"></GridSplitter>
13         <Button Grid.Row="2" Content="Button"></Button>
14     </Grid>
15 </Window>

2.6 UniformGrid布局
UniformGrid就是Grid的简化版,每个单元格的大小相同,不需要定义行列集合。每个单元格始终具有相同的大小,每个单元格只能容纳一个控件。

若不设置RowsColums,则按照定义在其内部的元素个数,自动创建行列,并通常保持相同的行列数。若只设置Rows则固定行数,自动扩展列数。若只设置Colums则固定列数,自动扩展行数。

UniformGrid 中没有Row和Column附加属性,也没有空白单元格。

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <UniformGrid>
 7             <Button Content="Button"></Button>
 8             <Button Content="Button"></Button>
 9             <Button Content="Button"></Button>
10             <Button Content="Button"></Button>
11             <Button Content="Button"></Button>
12             <Button Content="Button"></Button>
13             <Button Content="Button"></Button>
14         </UniformGrid>
15     </Grid>
16 </Window>

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <UniformGrid Columns="2">
 7             <Button Content="Button"></Button>
 8             <Button Content="Button"></Button>
 9             <Button Content="Button"></Button>
10             <Button Content="Button"></Button>
11             <Button Content="Button"></Button>
12             <Button Content="Button"></Button>
13             <Button Content="Button"></Button>
14         </UniformGrid>
15     </Grid>
16 </Window>

2.7 ScrollViewer布局
ScrollViewer是带有滚动条的面板。在ScrollViewer中只能有一个子控件,若要显示多个子控件,需要将一个附加的 Panel控件放置在父 ScrollViewer中。然后可以将子控件放置在该控件中。
HorizontalScrollBarVisibility水平滚动条是否显示默认为Hidden
VerticalScrollBarVisibility垂直滚动条是否显示 默认为Visible。
一般我们都会设置 HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
意思是:当内容超出可视范围时,才显示横向/纵向滚动条

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
 7             <Button Content="Button" Width="800" Height="800"></Button>
 8         </ScrollViewer>
 9     </Grid>
10 </Window>

2.8 ViewBox布局
Viewbox的作用是拉伸或延展位于其中的组件,以填满可用空间。在Viewbox中只能有一个子控件,若要显示多个子控件,需要将一个附加的Panel控件放置在父Viewbox中。然后可以将子控件放置在该控件中。

常用属性:

Stretch:获取或设置拉伸模式以决定该组件中的内容以怎样的形式填充该组件的已有空间。具体设置值如下:

None:不进行拉伸,按子元素设置的长宽显示。

Uniform:按原比例缩放子元素,使得一边不足,另一边恰好填充

Fill:缩放子元素,使得子元素的长变为Viewbox的长,宽变为Viewbox的宽

UniformToFill:按原比例缩放子元素,使得子元素一边恰好填充,另一边超出Viewbox的区域

Stretch默认值为Uniform。

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <Grid.RowDefinitions>
 7             <RowDefinition>
 8
 9             </RowDefinition>
10         </Grid.RowDefinitions>
11         <Grid.ColumnDefinitions>
12             <ColumnDefinition>
13
14             </ColumnDefinition>
15         </Grid.ColumnDefinitions>
16         <Viewbox Grid.Row="0" Grid.Column="0" Stretch="None">
17             <Button Width="100" Height="50" Content="None"></Button>
18         </Viewbox>
19         <Viewbox Grid.Row="0" Grid.Column="1" Stretch="Uniform">
20             <Button Width="100" Height="50" Content="Uniform"></Button>
21         </Viewbox>
22         <Viewbox Grid.Row="1" Grid.Column="0" Stretch="Fill">
23             <Button Width="100" Height="50" Content="Fill"></Button>
24         </Viewbox>
25         <Viewbox Grid.Row="1" Grid.Column="1" Stretch="UniformToFill">
26             <Button Width="100" Height="50" Content="UniformToFill"></Button>
27         </Viewbox>
28     </Grid>
29 </Window>

2.9 Border
Border 是一个装饰的控件,此控件用于绘制边框及背景,在Border中只能有一个子控件,若要显示多个子控件,需要将一个附加的Panel控件放置在父Border中。然后可以将子控件放置在该 Panel控件中。

常用属性:

Background: 背景色 ;

BorderBrush: 边框色 ;

BorderThickness: 边框宽度;

CornerRadius: 各个角 圆的半径;

1 <Window x:Class="WPF_Layout.MainWindow"
2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4         Title="WPF-Layout" Height="350" Width="525">
5     <Grid>
6         <Border Background="YellowGreen" BorderBrush="Black" BorderThickness="0, 2, 4, 6" CornerRadius="0, 10, 20, 30"></Border>
7     </Grid>
8 </Window>

三 精确定位的常用属性
在设计UI时,WPF为我们提供了一些属性用于精确定位元素,其中最常用的有三个:Alignment(包括水平,垂直),Margin,Padding,具体用法如下:

HorizontalAlignment: 子元素在水平方向的对齐方式,有左对齐,右对齐,中间对齐,拉伸填充等四种方式。

VerticalAlignment:子元素在垂直方向的对齐方式,有顶端对齐,底部对齐,中间对齐,拉伸填充等四种方式。

Margin:用于指定元素与其父级或同级之间的距离,包括上下左右四个值。也可通过使用 Margin="20"同时指定四个值。

Padding:用于指定元素与其子级之间的距离,包括上下左右四个值。也可通过使用Padding="20"同时指定四个值。

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <Grid.RowDefinitions>
 7             <RowDefinition>
 8
 9             </RowDefinition>
10             <RowDefinition>
11
12             </RowDefinition>
13         </Grid.RowDefinitions>
14         <Button Grid.Row="0" Content="Center" HorizontalAlignment="Center"></Button>
15         <Button Grid.Row="1" Content="Left" HorizontalAlignment="Left"></Button>
16         <Button Grid.Row="2" Content="Right" HorizontalAlignment="Right"></Button>
17         <Button Grid.Row="3" Content="Stretch" HorizontalAlignment="Stretch"></Button>
18     </Grid>
19 </Window>

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <Border Background="YellowGreen">
 7             <Button Margin="0, 50, 100, 150"></Button>
 8         </Border>
 9     </Grid>
10 </Window>

 1 <Window x:Class="WPF_Layout.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="WPF-Layout" Height="350" Width="525">
 5     <Grid>
 6         <Border Background="YellowGreen" Padding="0, 50, 100, 150">
 7             <Button ></Button>
 8         </Border>
 9     </Grid>
10 </Window>

 
 
 

栈的实现

 

一 写在开头

1.1 本文内容

数据结构栈的实现。

二 栈的实现

不多说了,请直接看代码。

 1 // stack.h
 2
 3 /* 为了使用bool */
 4 #include <stdbool.h>
 5
 6 #ifndef _H_STACK_H
 7 #define _H_STACK_H
 8
 9 /* 数据类型 */
10 typedef char type;
11
12 /* 栈结点 */
13 typedef struct tsNode
14 {
15     type data;
16     struct tsNode *next;
17 } Node;
18
19 /* 栈类型 */
20 typedef struct tsStack
21 {
22     Node *top;
23     unsigned int size;
24 } Stack;
25
26 /* 初始化Stack */
27 Stack *
28 StackInit(void);
29
30 /* 判断栈是否为空。为空返回true,否则返回false */
31 bool
32 IsEmpty(Stack *);
33
34 /* 入栈操作 */
35 void
36 StackPush(Stack *, type);
37
38 /* 出栈操作 */
39 type
40 StackPop(Stack *);
41
42 /* 取得栈顶元素 */
43 type
44 GetTop(Stack *);
45 #endif
 1 // stack.c
 2
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <stdbool.h>
 6 #include "stack.h"
 7
 8 /* 功能:栈初始化
 9  * 输入:无
10  * 输出:一个指向Stack型的指针,指针所指对象已被初始化
11  */
12 Stack *
13 StackInit(void)
14 {
15     Stack *s = NULL;
16
17     s = (Stack *)malloc(sizeof(Stack));
18     if (s == NULL)
19         exit(EXIT_FAILURE);
20     s->top = NULL;
21     s->size = 0;
22     return s;
23 }
24
25 /* 功能:判断栈是否为空。为空返回true,否则返回false
26  * 输入:一个指向Stack型的指针
27  * 输出:一个bool型变量
28  */
29 bool
30 IsEmpty(Stack *s)
31 {
32     /* s未被初始化 */
33     if (s == NULL)
34         exit(EXIT_FAILURE);
35     else
36         return ((s->size) == 0);
37 }
38
39 /* 功能:入栈操作
40  * 输入:一个指向Stack型的指针,一个type型待入栈变量
41  * 输出:无
42  */
43 void
44 StackPush(Stack *s, type x)
45 {
46     Node *temp = (Node *)malloc(sizeof(Node));
47
48     if (temp == NULL)
49         exit(EXIT_FAILURE);
50     temp->data = x;
51
52     /* 若s未被初始化 */
53     if (s == NULL)
54         exit(EXIT_FAILURE);
55     temp->next = s->top;
56     s->top = temp;
57     s->size = (s->size) + 1;
58 }
59
60 /* 功能:出栈操作
61  * 输入:一个指向Stack型的指针
62  * 输出:被出栈的元素
63  */
64 type
65 StackPop(Stack *s)
66 {
67     Node *p = NULL;
68     type r;
69
70     if (IsEmpty(s))
71         exit(EXIT_FAILURE);
72     p = s->top->next;
73     r = s->top->data;
74     free(s->top);
75     s->top = p;
76     s->size = (s->size) - 1;
77     return r;
78 }
79
80 /* 功能:返回栈顶元素
81  * 输入:一个指向Stack型的指针
82  * 输出:栈顶元素
83  */
84 type
85 GetTop(Stack *s)
86 {
87     if (IsEmpty(s))
88         exit(EXIT_FAILURE);
89     return s->top->data;
90 }
 1 #include <stdio.h>
 2 #include "stack.h"
 3
 4 int main(int argc, char *argv[])
 5 {
 6     Stack *s = StackInit();
 7
 8     /* test passed
 9     printf("s->top = %p\n", s->top);
10     printf("s->size = %u\n", s->size);
11     printf("s is empty: ");
12     IsEmpty(s) ? printf("yes\n") : printf("no\n");
13     */
14
15     /* test passed
16     StackPush(s, 'h');
17     StackPush(s, 'e');
18     StackPush(s, 'l');
19     StackPush(s, 'l');
20     StackPush(s, 'o');
21     printf("s is empty: ");
22     IsEmpty(s) ? printf("yes\n") : printf("no\n");
23     printf("s->size = %u\n", s->size);
24
25     while ((s->size) > 0)
26     {
27         printf("%c\n", StackPop(s));
28     }
29     printf("s is empty: ");
30     IsEmpty(s) ? printf("yes\n") : printf("no\n");
31     */
32
33     /* test passed
34     StackPush(s, 'h');
35     StackPush(s, 'e');
36     StackPush(s, 'l');
37     StackPush(s, 'l');
38     StackPush(s, 'o');
39     while (IsEmpty(s) == false)
40     {
41         printf("%c\n", GetTop(s));
42         StackPop(s);
43     }
44     printf("s is empty: ");
45     IsEmpty(s) ? printf("yes\n") : printf("no\n");
46     */
47
48     // 栈的内存组织情况 - pass
49     /* 当s为空栈时内存组织情况
50      * p s - 0x602010
51      * p s->top - 0x0
52      * p s->size - 0
53      */
54
55     StackPush(s, 'h');
56     /* 当s中有一个值为'h'的元素时内存组织情况
57      * p s - 0x602010
58      * p s->top - 0x602030
59      * p s->size - 1
60      * p s->top->data - 'h'
61      * p s->top->next - 0x0
62      */
63
64     StackPush(s, 'e');
65     /* 当s中有两个元素时内存组织情况
66      * p s - 0x602010
67      * p s->top - 0x602050
68      * p s->size - 2
69      * p s->top->data - 'e'
70      * p s->top->next - 0x602030
71      * p s->top->next->data - 'h'
72      * p s->top->next->next - 0x0
73      */
74
75     StackPop(s);
76     /* 当将栈顶元素弹出后内存组织情况
77      * p s - 0x602010
78      * p s->top - 0x602030
79      * p s->size - 1
80      * p s->top->data - 'h'
81      * p s->top->next - 0x0
82      */
83
84     return 0;
85 }

注意:为了成功编译包含了stack.h的主程序,你需要使用下面的命令。也即必须将stack.c编译成目标文件,供链接程序链接使用,否则链接程序将报错。不同于C标准库的使用,因为C标准库默认采用动态链接的方式,所用标准库中的函数均已经被编译好了,放在特定的目录下面,链接程序只需去目录中找相对应的目标文件即可。这里我们必须将stack.c编译成目标文件,供主程序链接时使用。

gcc main.c stack.c

三 栈的一个应用实例

来做题吧!题目来源在此。需要说明的是下面的代码质量不好,因为代码是在上面的栈代码基础上拼凑而来的。为了记录每个字符在输入字符串中的位置,在栈结点中添加了一个index字段用于记录结点位置。算法原理很简单:将每一个字符入栈,判断入栈的字符是否为右括号(')'),如果是则将左右括号出栈并将二者在字符串中的位置按题目要求的顺序打印出来即可。

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <stdbool.h>
  5 #define ARR_MAX_LEN 128
  6 static char array[ARR_MAX_LEN];
  7
  8 /* 数据类型 */
  9 typedef char type;
 10
 11 /* 栈结点 */
 12 typedef struct tsNode
 13 {
 14     int index;
 15     type data;
 16     struct tsNode *next;
 17 } Node;
 18
 19 /* 栈类型 */
 20 typedef struct tsStack
 21 {
 22     Node *top;
 23     unsigned int size;
 24 } Stack;
 25
 26 /* 初始化Stack */
 27 Stack *
 28 StackInit(void);
 29
 30 /* 判断栈是否为空。为空返回true,否则返回false */
 31 bool
 32 IsEmpty(Stack *);
 33
 34 /* 入栈操作 */
 35 void
 36 StackPush(Stack *, int, type);
 37
 38 /* 出栈操作 */
 39 type
 40 StackPop(Stack *);
 41
 42 /* 取得栈顶元素 */
 43 Node *
 44 GetTop(Stack *);
 45
 46 /* 功能:栈初始化
 47  * 输入:无
 48  * 输出:一个指向Stack型的指针,指针所指对象已被初始化
 49  */
 50 Stack *
 51 StackInit(void)
 52 {
 53     Stack *s = NULL;
 54
 55     s = (Stack *)malloc(sizeof(Stack));
 56     if (s == NULL)
 57         exit(EXIT_FAILURE);
 58     s->top = NULL;
 59     s->size = 0;
 60     return s;
 61 }
 62
 63 /* 功能:判断栈是否为空。为空返回true,否则返回false
 64  * 输入:一个指向Stack型的指针
 65  * 输出:一个bool型变量
 66  */
 67 bool
 68 IsEmpty(Stack *s)
 69 {
 70     /* s未被初始化 */
 71     if (s == NULL)
 72         exit(EXIT_FAILURE);
 73     else
 74         return ((s->size) == 0);
 75 }
 76
 77 /* 功能:入栈操作
 78  * 输入:一个指向Stack型的指针,一个type型待入栈变量
 79  * 输出:无
 80  */
 81 void
 82 StackPush(Stack *s, int index, type x)
 83 {
 84     Node *temp = (Node *)malloc(sizeof(Node));
 85
 86     if (temp == NULL)
 87         exit(EXIT_FAILURE);
 88     temp->index = index;
 89     temp->data = x;
 90
 91     /* 若s未被初始化 */
 92     if (s == NULL)
 93         exit(EXIT_FAILURE);
 94     temp->next = s->top;
 95     s->top = temp;
 96     s->size = (s->size) + 1;
 97 }
 98
 99 /* 功能:出栈操作
100  * 输入:一个指向Stack型的指针
101  * 输出:被出栈的元素
102  */
103 type
104 StackPop(Stack *s)
105 {
106     Node *p = NULL;
107     type r;
108
109     if (IsEmpty(s))
110         exit(EXIT_FAILURE);
111     p = s->top->next;
112     r = s->top->data;
113     free(s->top);
114     s->top = p;
115     s->size = (s->size) - 1;
116     return r;
117 }
118
119 /* 功能:返回栈顶元素
120  * 输入:一个指向Stack型的指针
121  * 输出:栈顶元素
122  */
123 Node *
124 GetTop(Stack *s)
125 {
126     if (IsEmpty(s))
127         exit(EXIT_FAILURE);
128     return s->top;
129 }
130
131 int
132 main(int argc, char *argv[])
133 {
134     //freopen("in.txt", "r", stdin);
135     int i, len, temp;
136     Stack *s = StackInit();
137
138     scanf("%s", array);
139     len = strlen(array);
140     for (i = 0; i < len; i++)
141     {
142         StackPush(s, i, array[i]);
143         if ((GetTop(s))->data == ')')
144         {
145             temp = GetTop(s)->index;
146             StackPop(s);
147             printf("%d %d\n", (GetTop(s))->index, temp);
148             StackPop(s);
149         }
150     }
151
152     //fclose(stdin);
153     return 0;
154 }

一个关于素数的神奇性质

 

一 写在前面

1.1 本文内容

一个关于素数的神奇性质。

二 素数性质

性质:所有大于等于5的素数一定和6的倍数相邻!此性质可以被证明,证明方法可以去搜索相关资料。下面给出1000以内的素数,你可以验证一下看是不是这样。

有了这个性质,下面再给出一个其在质因数分解中的实际应用例子。题目链接在此。题目大意是给定一个正整数N,要求将其分解成多个质因数相乘的形式。需要注意的是质因数分解的常用算法是试除法,例子中使用的也是试除法。并且每一个合数都能够写成几个质因数相乘的形式。

上述题目的一个可行代码如下:

 1 #include <stdio.h>
 2 #include <stdbool.h>
 3
 4 int main(int argc, char *argv[])
 5 {
 6     //freopen("in.txt", "r", stdin);
 7     int n, step;
 8     bool first = true;
 9
10     scanf("%d", &n);
11
12     /* 将数n中包含的2, 3, 5这三个质因数给除去 */
13     while (n % 2 == 0)
14     {
15         (first == true) ? printf("2") : printf("*2");
16         first = false;
17         n /= 2;
18     }
19     while (n % 3 == 0)
20     {
21         (first == true) ? printf("3") : printf("*3");
22         first = false;
23         n /= 3;
24     }
25     while (n % 5 == 0)
26     {
27         (first == true) ? printf("5") : printf("*5");
28         first = false;
29         n /= 5;
30     }
31
32     /* 让step从6开始,考察step相邻的两个数,如果能整除n,则将其除去 */
33     step = 6;
34     while (n >= (step-1))
35     {
36         if (n % (step-1) == 0)
37         {
38             (first == true) ? printf("%d", step-1) : printf("*%d", step-1);
39             first = false;
40             n /= (step-1);
41         }
42         else if (n % (step+1) == 0)
43         {
44             (first == true) ? printf("%d", step+1) : printf("*%d", step+1);
45             first = false;
46             n /= (step+1);
47         }
48         else
49         {
50             step += 6;
51         }
52     }
53
54     putchar('\n');
55     //fclose(stdin);
56     return 0;
57 }

有人可能有疑问,虽然大于等于5的素数一定和6的倍数相邻,但反过来,所有与6的倍数相邻的却不一定是素数。也就是说在代码中的最后一个while循环中,step-1和step+1两个数可能是合数,那这样岂不是将n中的合数因子给除去了吗?事实果真是这样吗?不是的,前面说过,任何一个合数一定可以分解成几个质因数相乘的形式,也就是说即使step-1和step+1两个数中存在合数,那么它也无法整除n,因为在遇到该合数之前,该合数所能分解成的所有质因数都已经在n中给去除了。换句话说,在遇到n的合数因子(step-1或step+1)之前,该合数因子就因为其自身能被分解成更小的质因数而在之前的质因数分解中被分解殆尽了。所以上述代码是没问题的。

C# defualt关键字默认值用法

 

默认值表达式生成类型的默认值。 默认值表达式在泛型类和泛型方法中非常有用。 使用泛型类和泛型方法时出现的一个问题是,如何在无法提前知道以下内容的情况下将默认值赋值给参数化类型 T

  • T 是引用类型还是值类型。
  • 如果 T 是值类型,它是数值还是用户定义的结构。

已知参数化类型 T 的变量 t,仅当 T 为引用类型时,语句 t = null 才有效。 赋值 t = 0 仅对数值类型有效,对结构无效。 解决方案是使用默认值表达式,该表达式对引用类型(类类型和接口类型)返回 null,对数值类型返回零。 对于用户定义的结构,返回初始化为零位模式的结构,该结构根据成员是值还是引用类型,为每个成员生成 0 或 null。 对于可为 NULL 的值类型,default 返回像任何结构一样初始化的System.Nullable<T>

default(T) 表达式不限于泛型类和泛型方法。 默认值表达式可用于任何托管类型

以上内容来自MSDN。

示例代码:
 
int iValue = default(int);
float fValue = default(float);
decimal dValue = default(decimal);
Console.Write("值类型测试:");
Console.WriteLine("{0},{1},{2}", iValue, fValue, dValue);

string strValue = default(string);
DateTime? dt = default(DateTime?);
User user = default(User);
Console.Write("引用类型测试:");
Console.WriteLine("{0},{1},{2}", (null == strValue), (null == user), (null == dt));
Console.WriteLine();

 
 
 
 
接口
说起接口那生活中处处是接口。比如我们最常用的USB线,那就是一个接口线,现在只要是个手机基本都是通用的USB。比如水龙头接口,水龙头坏了只是换水龙头,不可能把管子也换了。那说明什么,其实接口就是一种通用标准,你只要符合这种标准,拿来就能用。你只要是两孔插头,随便找个两孔插板都能用。那这有跟我们编程又有什么关系?哎,如果不是没事瞎想,还真想不来,所有有时候编程还是要多去跟生活结合着想一想,对我们还是有好处的。那到了编程中其实也是这种思想,就是一种标准。比如我们有一个鼠标类,然后有个电脑类,电脑厂家说我给你们把usb口留好了,你们自己去做usb对接就行。那造鼠标键盘的商家就按照usb接口制造了这些配件。那换到程序中就是,鼠标键盘这些类去实现usb接口就行,这样只要遇到usb接口就都能对接,因为都是一样的方法,至于里面怎么实现功能,那就是鼠标和键盘类的事了。其实通俗不标准的说,就是在代码中定义一种标准的方法的集合。如果哪个类要跟这个标准对接,那么你只要实现这个接口就行,基本就这么个意思。
 
 
 

C# Json序列化和反序列化

 
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。
 
方法一、使用 Newtonsoft.Json.dll
核心代码:
public static class JsonExtension
{
    /// <summary>
    /// object转json字符串
    /// </summary>
    public static string ObjectToJson(this object obj)
    {
        if (null == obj)
        {
            return null;
        }

return JsonConvert.SerializeObject(obj);
    }

/// <summary>
    /// json字符串转object
    /// </summary>
    public static T JsonToObject<T>(this string jsonString)
    {
        try
        {
            return JsonConvert.DeserializeObject<T>(jsonString);
        }
        catch
        {
            return default(T);
        }
    }

/// <summary>
    /// 格式化json字符串
    /// </summary>
    public static string ConvertJsonString(this string str)
    {
        // 格式化json字符串
        JsonSerializer serializer = new JsonSerializer();
        TextReader tr = new StringReader(str);
        JsonTextReader jtr = new JsonTextReader(tr);
        object obj = serializer.Deserialize(jtr);
        if (null != obj)
        {
            StringWriter textWriter = new StringWriter();
            JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
            {
                Formatting = Formatting.Indented,
                Indentation = 4,
                IndentChar = ' '
            };
            serializer.Serialize(jsonWriter, obj);
            return textWriter.ToString();
        }
        else
        {
            return str;
        }
    }

}

 

 
方法二、利用系统的 DataContractJsonSerializer 类
引用 System.Runtime.Serialization;
名字空间using System.Runtime.Serialization.Json;
核心代码:
public static class JsonExtension
{
    public static string GetJsonByObject<T>(this T obj)
    {
        if (null == obj)
        {
            return null;
        }

//实例化DataContractJsonSerializer对象,需要待序列化的对象类型
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));

//实例化一个内存流,用于存放序列化后的数据
        using (MemoryStream stream = new MemoryStream())
        {
            //使用WriteObject序列化对象
            serializer.WriteObject(stream, obj);

//写入内存流中
            byte[] dataBytes = new byte[stream.Length];
            stream.Position = 0;
            stream.Read(dataBytes, 0, (int)stream.Length);

//通过UTF8格式转换为字符串
            return Encoding.UTF8.GetString(dataBytes);
        }   
    }

public static T GetObjectByJson<T>(this string jsonString)
    {
        try
        {
            //实例化DataContractJsonSerializer对象,需要待序列化的对象类型
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));

//把Json传入内存流中保存
            using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
            {
                // 使用ReadObject方法反序列化成对象
                return (T)serializer.ReadObject(stream);
            }
        }
        catch (Exception)
        {
            return default(T);
        }
        
    }

}

 
 
 
 
 
 

ASP.NET CORE系列【五】webapi整理以及RESTful风格化

 

介绍

什么是RESTful?  这里不多做赘述,详情请百度!

哈哈,本来还想巴拉巴拉介绍一些webapi, RESTful的, 还是算了,咱们直接上干货!(原因是懒!哈哈)

使用

以前使用过mvc的人对webapi 应该都很熟悉,先看一段熟悉的代码

大伙发现了什么没?跟以往mvc大多数相同,但有些地方不同 ,我们来一起看看有何区别

1.首先SysUsersController上面有一段代码

[Produces("application/json")]
    //[Route("api/SysUsers")]
    [Route("api/[controller]/[action]")]
    public class SysUsersController : Controller
    {
    }

注释的部分先不管,首先我们接收的json格式内容,然后路由是 api/[controller]/[action]

这种情况我们只需要正常的ajax请求就能访问到了,

 $.ajax({
                    url:'/api/SysUsers/Login',
                    type: 'POST',
                    contentType: "application/json; charset=utf-8",
                    data: {UserName:'shumin',Password:'123456'},
                    success: function (data) {
                        if (data.success) {
                            var href = '@Url.Action("Index")?' + new Date().getTime();
                            window.location.href = href;
                        } else {
                            alert(data.message);
                        }
                    },
                    error: function () {
                        alert('服务端错误');
                    }
                });

我们运行一下, 发现拿不到数据,

这是为什么呢,经过多番尝试,是前台http请求不对,因为我们约定的json传输,所以数据需要序列化

 $.ajax({
                    url:'/api/SysUsers/Login',
                    type: 'POST',
                    contentType: "application/json; charset=utf-8",
                    data: JSON.stringify({UserName:'shumin',Password:'123456'}),
                    success: function (data) {
                        if (data.success) {
                            var href = '@Url.Action("Index")?' + new Date().getTime();
                            window.location.href = href;
                        } else {
                            alert(data.message);
                        }
                    },
                    error: function () {
                        alert('服务端错误');
                    }
                });

还有一点需要注意的事,ajax默认的

contentType是application/x-www-form-urlencoded

我们需要改成application/json; charset=utf-8

这样就可以拿到数据了

RESTful

推荐一个关于RESTful的介绍文章 http://www.ruanyifeng.com/blog/2014/05/restful_api

用上面那种方法,会导致URL又臭又长,举个例子

网站:/get_user?id=3
  RESTFul: GET /user/3 (GET是HTTP类型)

以前的时候我们写http请求,只会用get  post两种,

而注册对应的资源是user,api如下:

  GET /user/:id # 获取id用户的信息 
  POST /user # 创建新的用户(注册) 
  PUT /user/:id # 更新id用户的信息 
  DELETE /user/:id # 删除id用户(注销)

我们一起来看看RESTFul API有哪些特点:

  1. 基于“资源”,数据也好、服务也好,在RESTFul设计里一切都是资源。
  2. 无状态。一次调用一般就会返回结果,不存在类似于“打开连接-访问数据-关闭连接”这种依赖于上一次调用的情况。
  3. URL中通常不出现动词,只有名词
  4. URL语义清晰、明确
  5. 使用HTTP的GET、POST、DELETE、PUT来表示对于资源的增删改查
  6. 使用JSON不使用XML

我们接着来看一看RESTFul API的一些最佳实践原则:

  1. 使用HTTP动词表示增删改查资源, GET:查询,POST:新增,PUT:更新,DELETE:删除
  2. 返回结果必须使用JSON
  3. HTTP状态码,在REST中都有特定的意义:200,201,202,204,400,401,403,500。比如401表示用户身份认证失败,403表示你验证身份通过了,但这个资源你不能操作。

这里直接贴出RESTful的api仅供参考

namespace NetCoreAdmin.Controllers
{
    [Produces("application/json")]
    [Route("api/SysUsers")]
    public class SysUsersController : Controller
    {
        private readonly EFCoreContext _context;

        public SysUsersController(EFCoreContext context)
        {
            _context = context;
        }

        // GET: api/SysUsers
        [HttpGet]
        public IEnumerable<SysUser> GetSysUsers()
        {
            return _context.SysUsers;
        }

        // GET: api/SysUsers/5
        [HttpGet("{id}")]
        public async Task<IActionResult> GetSysUser([FromRoute] int id)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var sysUser = await _context.SysUsers.SingleOrDefaultAsync(m => m.Id == id);

            if (sysUser == null)
            {
                return NotFound();
            }

            return Ok(sysUser);
        }

        // PUT: api/SysUsers/5
        [HttpPut("{id}")]
        public async Task<IActionResult> PutSysUser([FromRoute] int id, [FromBody] SysUser sysUser)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != sysUser.Id)
            {
                return BadRequest();
            }

            _context.Entry(sysUser).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!SysUserExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // POST: api/SysUsers
        [HttpPost]
        public async Task<IActionResult> PostSysUser([FromBody] SysUser sysUser)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            _context.SysUsers.Add(sysUser);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetSysUser", new { id = sysUser.Id }, sysUser);
        }

        // DELETE: api/SysUsers/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteSysUser([FromRoute] int id)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var sysUser = await _context.SysUsers.SingleOrDefaultAsync(m => m.Id == id);
            if (sysUser == null)
            {
                return NotFound();
            }

            _context.SysUsers.Remove(sysUser);
            await _context.SaveChangesAsync();

            return Ok(sysUser);
        }

        private bool SysUserExists(int id)
        {
            return _context.SysUsers.Any(e => e.Id == id);
        }
    }
}
 
 

WPF中的常用布局 栈的实现 一个关于素数的神奇性质 C# defualt关键字默认值用法 接口通俗理解 C# Json序列化和反序列化 ASP.NET CORE系列【五】webapi整理以及RESTful风格化的更多相关文章

  1. WPF中的常用布局

    一 写在开头1.1 写在开头评价一门技术的好坏得看具体的需求,没有哪门技术是面面俱到地好. 1.2 本文内容本文主要内容为WPF中的常用布局,大部分内容转载至https://blog.csdn.net ...

  2. asp.net core系列 38 WebAPI 返回类型与响应格式--必备

    一.返回类型 ASP.NET Core 提供以下 Web API Action方法返回类型选项,以及说明每种返回类型的最佳适用情况: (1) 固定类型 (2) IActionResult (3) Ac ...

  3. asp.net core系列 36 WebAPI 搭建详细示例

    一.概述 HTTP不仅仅用于提供网页.HTTP也是构建公开服务和数据的API强大平台.HTTP简单灵活且无处不在.几乎任何你能想到的平台都有一个HTTP库,因此HTTP服务可以覆盖广泛的客户端,包括浏 ...

  4. asp.net core系列 37 WebAPI 使用OpenAPI (swagger)中间件

    一.概述 在使用Web API时,对于开发人员来说,了解其各种方法可能是一项挑战.在ASP.NET Core上,Web api 辅助工具介绍二个中间件,包括:Swashbuckle和NSwag .NE ...

  5. WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)

    WebAPI调用笔记   前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...

  6. asp.net core 系列之webapi集成Dapper的简单操作教程

    Dapper也是是一种ORM框架 这里记录下,使用ASP.NET 集成 Dapper 的过程,方便自己查看 至于Dapper的特性以及操作可以参考Dapper官方文档 1.创建数据库相关 在Sql S ...

  7. asp.net core 系列之webapi集成EFCore的简单操作教程

    因为官网asp.net core webapi教程部分,给出的是使用内存中的数据即 UseInMemoryDatabase 的方式, 这里记录一下,使用SQL Server数据库的方式即 UseSql ...

  8. ASP.NET CORE系列【五】webapi整理以及RESTful风格化

    介绍 什么是RESTful?  这里不多做赘述,详情请百度! 哈哈,本来还想巴拉巴拉介绍一些webapi, RESTful的, 还是算了,咱们直接上干货!(原因是懒!哈哈) 使用 以前使用过mvc的人 ...

  9. asp.net core系列 77 webapi响应压缩

    一.介绍 背景:目前在开发一个爬虫框架,使用了.net core webapi接口作为爬虫调用入口,在调用 webapi时发现爬虫耗时很短(1秒左右),但客户端获取响应时间却在3~4秒.对于这个问题考 ...

随机推荐

  1. BEGIN - 开始一个事务块

    SYNOPSIS BEGIN [ WORK | TRANSACTION ] DESCRIPTION 描述 BEGIN 初始化一个事务块, 也就是说所有 BEGIN 命令后的用户语句都将在一个事务里面执 ...

  2. 02Document Type Definition

    Document Type Definition 1. Document Type Definition DTD(Document Type Definition)文件格式定义作用是给予文件一种格(T ...

  3. JavaSE-12 面向对象程序设计的几条基础原则

    摘取代码中变化的行为,形成接口 在设计基类的时候,如果该类某个成员方法在子类中的实现变化差别比较大(一部分子类实现该方法是相同的),作为基类有两个问题:一是该方法不再通用:二是子类如果重写该方法,存在 ...

  4. mxnet.base.MXNetError: src/imperative/./imperative_utils.h:70: Check failed: inputs[i]->ctx().dev_mask() == ctx.dev_mask() (1 vs. 2)

    mxnet 训练错误: mxnet.base.MXNetError: [14:42:22] src/imperative/./imperative_utils.h:70: Check failed: ...

  5. EZOJ 宝石迷阵 建图+网络流匹配

    题面: 封印恶魔的地方可以看作是一个 n*m 的矩形,包含了 n*m 个祭坛,并且其 中有一些祭坛已经损坏了. 如果 i+j 为偶数,那么第 i 行第 j 列的祭坛只要没有损坏,就一定会封印有一个恶魔 ...

  6. CentOS7.4 搭建和使用telnet

    1.先检查是否安装了telnet rpm -qa | grep telnet  //检查你的CentOS是否安装了telnet和telnet-server rpm -qa xinetd //检查你的C ...

  7. docker push

    一.确保docker hub上有账号 二.确认要提交的镜像的命名空间为自己账号名称 三.在本地登录docker: docker login 四.提交镜像: docker push zhengchuzh ...

  8. buf.toString()

    buf.toString([encoding[, start[, end]]]) encoding {String} 默认:'utf8' start {Number} 默认:0 end {Number ...

  9. PDO、PDOStatement、PDOException

    最近在学PDO  比较详细的资料 出处:http://blog.csdn.net/hsst027/article/details/23682003 PDO中包含三个预定义的类,它们分别是PDO.PDO ...

  10. Python Pandas库的学习(三)

    今天我们来继续讲解Python中的Pandas库的基本用法 那么我们如何使用pandas对数据进行排序操作呢? food.sort_values("Sodium_(mg)",inp ...