CSS学习笔记之基础与选择器

thbcm阅读(221)

这是一篇关于CSS基础跟选择器的笔记,毕竟自己的记性没有笔记本好用,所以记录一下,以后忘了也好有地方翻一下,复习重温。

CSS介绍

我们可以使用HTML构建稳定的结构基础,而页面的风格样式控制则交给CSS来完成。网页的样式包括各种元素的颜色、大小、线形、间距等等,这对于设计或维护一个数据较多的网站来说,工作量是巨大的。好在可以使用CSS来控制这些样式,这将大大提高网页设计和维护的效率,并且使网页的整体风格很容易做到统一。

CSS概述

CSS是英文Cascading Style Sheet的缩写,中文译为层叠样式表,也有人翻译为级联样式表,简称样式表。它是一种用来定义网页外观样式的技术,在网页中引入CSS规则,可以快捷高效地对页面进行布局设计,可以精确的控制HTML标记对象的宽度、高度、位置、字体、背景等外观效果。 CSS是一种标识性语言,不仅可以有效的控制网页的样式,更重要的是实现了网页内容与样式的分离,并允许将CSS规则单独存放于一个文档中, CSS文件的扩展名为“css”。

CSS3

CSS3标准早在1995年就开始制订, 2001年提上W3C研究议程,但是,10年来CSS3可以说是基本上没有什么很大的变化,一直到2011年6月才发布了全新版本的CSS3,目前,许多浏览器都广泛支持CSS3CSS3CSS技术的一个升级版本,CSS3语言CSS划分为更小的模块,在朝着模块化的方向发展。以前的版本是一个比较庞大而且比较复杂模块,所以,把它分解成为一个个小的简单的模块,同时也加入了更多新的模块。在CSS3中有字体、颜色、布局、背景、定位、边框、多列、动画、用户界面等等多个模块。

CSS的基本用法

CSS的使用规则由两部分组成:选择器和一条或多条声明。其基本基本语法如下:

        选择器{
            属性1: 值;
            属性2: 值;
            …
            属性n: 值;
        }

CSS属性

CSS的属性按照相关功能进行了分组,包含了字体、文本、背景、列表、动画等多个分组,这些属性的具体使用方法和示例将会在后续中提到。

在HTML文档中使用CSS的方法

根据CSSHTML文档中的使用方法和作用范围不同,CSS样式表的使用方法分为三大类:行内样式、内部样式表和外部样式表,而外部样式表又可分为链入外部样式表和导入外部样式表。本节我们从四个分类来认识在HTML中使用CSS的方法。

  • 行内样式
  • 内部样式表
  • 外部样式表
  • 链入外部样式表
  • 导入外部样式表

行内样式

行内样式(inline style),也叫内联样式,它是CSS四种使用方法中最为直接的一种,它的实现借用HTML元素的全局属性style,把CSS代码直接写入其中即可。 严格意义上行内样式是一种不严谨的使用方式,它不需要选择器,这种方式下CSS代码和HTML代码混合在一起,因此不推荐使用行内样式。行内样式的基本语法如下:

        <标记 style="属性:值; 属性:值; …">

内部样式表

当单个文档需要特殊的样式时,应该使用内部样式表。内部样式表是将样式放在页面的head区里,这样定义的样式就应用到本页面中了,内部样式表使用style标记进行声明,是较为常用的一种使用方法。其基本语法如下:

    <head>
        <meta charset="utf-8" />
        <title></title>
        <style type="text/css">
            选择器1{属性:值;…}
            选择器2{属性:值;…}
            ……
            选择器n{属性:值;…}
        </style>
    </head>

style标记定义HTML文档的样式信息,规定的是HTML元素如何在浏览器中呈现,其中type用来指定元素中的内容类型。

链入外部样式表

当为了保证站点的风格统一,或当定义样式内容较多,且需要多个页面共享样式时,可使用外部样式表。链入外部样式表是把样式表保存为一个外部样式表文件,然后在页面中用link标记链接到这个样式表文件,link标记放在页面的head区内。其基本语法为:

    <head>
        <meta charset="utf-8" />
        <title></title>
        <link href="样式表路径" rel="stylesheet" type="text/css" />
    </head>

其中:

href:指出样式表存放的路径。

rel:用来定义链接的文件与HTML之间的关系,rel="stylesheet"是指在页面中使用这个外部的样式表。

type属性用于指定文件类型,“text/css”指文件的类型是样式表文本。

导入外部样式表

导入外部样式表是指在HTML文件头部的style元素里导入一个外部样式表,导入外部样式表采用import方式。导入外部样式表和链入样式表的方法很相似,但导入外部样式表的样式实质上相当于存在网页内部。其基本语法为:

    <head>
        <meta charset="utf-8" />
        <title></title>
        <style type="text/css">
            @import url("样式表路径");
        </style>
    </head>

CSS基本选择器

选择器是CSS中很重要的概念,它可以大幅度提高开发人员编写或修改样式表的工作效率。CSS3提供了大量的选择器,大体上可以分为基本选择器、组合选择器、属性选择器、伪类选择器和伪对象选择器等。由于浏览器支持情况,很多选择器在实际开发中很少用到,本篇主要记录最基本又最常用的几种选择器。 基本选择器包括标记选择器、类选择器、id选择器和通用选择器。

标记选择器

HTML文档中最基本的构成是HTML标记,如果要对文档中的所有同类标记都使用同一个CSS样式时,就应使用标记选择器。其基本语法为:

        标记名{ 属性1:值1; 属性2:值2;…}

例如要使所有P标签的文本居中时,语法如下:

            p{
                text-align: center;
            }

类选择器

类选择器的基本语法为:

        标记名.类名{属性1:值1;属性2:值2;…}

类选择器针对标记的全局属性class,引用方式为:

        <标记名 class="类名">

值得注意的是,这里的类名可以是任何合法的字符,由设计者定义。如果对所有的标记均可使用,则采用*.类名的形式,这里的*表示全部,当然,也可以省略。

下面举几个例子:

        <style type="text/css">
            p.text1{color:brown;font-size:14px;}
            /* 该形式下只允许<p>标记中类名为"text1"的标签引用该样式 */
            *.text1{ color:brown;font-size:14px; }
            或
            .text1{ color:brown;font-size:14px; }
            /* 表示所有类名为"text1"的标签都可引用该样式 */
        </style>

id选择器

id选择器和类选择器大致相同,不同的是定义时不使用“.”而使用“#”,作用于HTML标记的全局属性是“id”而不是“class”。 id选择器的基本语法为:

        标记名#id名{ 属性1: 值1;属性2: 值2;…}

id选择器针对标记的全局属性id,引用方式为:

        <标记名 id="id名">

当然,与类选择器一样,如果对所有的标记均使用时,则采用*#id名的形式,这里*表示全部,也可以省略。

通用选择器

通用选择器是一种特殊的选择器,用 * 表示,匹配网页中的所有元素,除非使用更为具体的选择器指定某一元素中对应的相同属性应使用其它值。通用选择器和对body元素设定样式稍有不同,因为通用选择器应用于每一个元素,而不依赖从应用于body元素的规则中继承的属性。其基本用法如下:

        <style type="text/css">
            *{
                属性1: 值1;
                属性2: 值2;
                …
            }
        </style>

其它CSS选择器

除了CSS基本选择器外,CSS还有许多其它选择器。

组合选择器

CSS中组合选择器,可以算作是基础选择器的升级版,也就是组合去使用基础选择器的意思。组合选择器主要有五个类别:多元素选择器、后代选择器、子选择器、相邻选择器和兄弟选择器。

多元素选择器

多元素选择器的基本语法为:

        E, F {属性1:值1;属性2:值2;… } 

这个很好理解,就是同时选中多个元素,中间用“,”隔开。

后代元素选择器

后代元素选择器的基本语法为:

        E F {属性1:值1;属性2:值2;… } 

这个也很好理解,就是匹配所有属于E元素后代的F元素E元素F元素用空格隔开,例如:

        table b{color:red; } 

就表示将表格中的所有b元素文字设置为红色。

子元素选择器

子元素选择器的基本语法为:

        E>F{属性1:值1;属性2:值2;…}

子元素选择器只能选择某元素的子元素,其中E为父元素,F为直接子元素,E>F所表示的是选择了E元素下的所有子元素F,其间用>连接。这和后代元素选择器不一样,在后代元素选择器中F是E的所有后代元素,而子元素选择器中F必须是E的子元素。

相邻兄弟选择器

相邻兄弟选择器的基本语法为:

        E+F{属性1:值1;属性2:值2;…}

相邻兄弟选择器可以选择紧接在另一元素后的元素,而且它们具有相同的父元素,其间用+号链接,换句话说,E和F具有同一个父元素,而且F元素在E元素后面并且紧紧相邻。

一般兄弟选择器

一般兄弟选择器的基本语法为:

        E~F{属性1:值1;属性2: 值2;…}

一般兄弟选择器将选择某元素后面的所有兄弟元素,其间用~号链接,它和相邻兄弟选择器类似,需要在同一个父元素之中,并且F元素在E元素之后。区别在于E ~ F 选择器匹配所有E元素后面的F元素,E+F仅匹配紧跟在E元素后边的F元素。

属性选择器

属性选择器是在标记后面加一个中括号,中括号中列出各种属性或者表达式。属性选择器的形式很多,我们这里通过示例简单介绍几个。

存在属性匹配

通过匹配存在的属性来控制元素的样式,一般要把匹配的属性包含在中括号中。 例如将任何带有href属性的a标记设置为综色:

        a[href]{color:brown;}

精确属性匹配 只有当属性值完全匹配指定的属性值时才会应用样式,id选择器和类选择器本质上就是精确属性匹配选择器。 例如将指向网址“http://www.w3cschool.cn” 的链接a标记设置为棕色:

        a[href="http://www.w3cschool.cn"]{color:brown;}

前缀匹配

只要属性值的开始字符串匹配指定字符串,即可对元素应用样式。前缀匹配使用[^=]形式实现,如:

        [id^="user"]{color:brown;}

那么下列的标签均可以变为棕色:

        <p id="userName">小明</p>
        <p id="userWeight">体重</p>
        <p id="userAge">年龄</p>

后缀匹配

与前缀匹配相反,只要属性值的结尾字符匹串配指定字符串,即可对元素应用样式。后缀匹配使用[$=]形式实现,如:

        [id$="Name"]{color:brown;}

那么下列的标签均可以变为棕色:

        <p id="JackName">杰克</p>
        <p id="RoseName">萝丝</p> 

子字符串匹配

只要属性中存在指定字符串即应用样式,使用[*=]形式实现,如:

        [id*="test"]{color:brown;}

那么下列的标签均可以变为棕色:

        <p id="Rosetest">段落1</p>
        <p id="testY">段落2</p> 
        <p id="xtesty">段落3</p>

以上就是CSS的学习笔记了,希望对大家有所帮助。然后对CSS感兴趣的同学可以看一下教程:

CSS教程:https://www.w3cschool.cn/css/

CSS微课:https://www.w3cschool.cn/minicourse/play/csscourse

如何在日常工作中合理使用VSCode Task

thbcm阅读(184)

在这篇文章你可以学到如何快速运行npm脚本,直接在VSCode中运行,并使用快捷方式。如果你没有使用过VSCode Task,那么你可以好好看一下。

为何使用VSCode Task

我工作过的所有 JavaScript 项目都有一组定义的脚本,你可以为一个应用程序执行。通常情况下,这些脚本都是命令,可以帮助你进行测试,构建或部署你的代码。我所合作过的大多数开发者都是用自己选择的命令行来运行这些命令。要么你必须死记硬背你的项目脚本,要么你的命令行可能有一些typeahead的功能,要么你就像我经常做的那样,搜刮历史记录来找到你过去运行的那个命令。

history | grep ‘npm run’

相反,你可以使用“Tasks”为您运行脚本。你可以先打开命令面板 Cmd + Shift + P ,然后选择“Tasks: Run Task”。

VSCode将为你提供它支持的多种任务类型。继续并选择“npm”。编辑器将快速扫描你的 package.json 并提供你定义的任务:

选择一个你的脚本,你就完成了!一个新的内置终端窗口被打开,你可以看到你的脚本的输出,并从你离开的地方继续工作。

好吧,这看起来很酷。但是你可能会想:“嘿,我的项目不是那么简单,我的任务中包含参数,不同的选项,也许我需要先打开子文件夹!”。

当然,你也可以这样做!

配置Tasks

假设你要为特定的测试文件运行单元测试,你的测试命令可能如下所示:

npm test 'my-component.js' --auto-watch --no-single-run

我通常的工作流程如下。我想在watch模式下运行我正在进行的单元测试。通常情况下,你需要在测试命令中插入文件名,但VSCode可以帮你完成。为了实现这个目标,我们可以使用一些为我们提供的替换变量。例如:${fileBasename}。可用变量的完整列表可以在这里的官方文档中找到。

现在,再次打开命令面板,选择“任务:运行任务”,然后选择“没有配置的任务。配置任务…”,然后选择要配置的任务。这将在项目中创建并打开一个新文件:.vscode/tasks.json。你可以将此文件添加到 .gitignore 或进行提交,因此你的团队也可以使用这些任务。

添加替换变量后,配置应如下所示:

{
  "version": "2.0.0",
    "tasks": [
      {
        "type": "npm",
        "script": "test ${fileBasename} --auto-watch --no-single-run",
        "problemMatcher": [],
        "label": "npm: test opened file",
        "detail": "npm test"
      }
    ]
}

然后,就这样,你的自定义任务就可以在命令面板中运行了。你的自定义任务现在就在你可以从Command Palette中运行的列表中。现在打开你要运行的测试文件,例如:my-component-test.js。运行 Cmd + Shift + P-> "Tasks: 运行任务",你应该会看到新配置的任务:”npm: test opened file“。选择它,它应该在终端中运行 npm test my-component-test.js --auto-watch --no-single-run。你还可以自定义脚本结果的显示方式。我想为这种类型的命令打开一个新的终端。为此,你只需要提供一个额外的 “演示 “配置即可。

{
  ...
  "presentation": {
    "panel": "dedicated",
  }
}

现在,你可以看到打开了多个终端窗口,可以在它们之间进行切换。

配置Shell Tasks

如果要执行其他Shell命令,VSCode也支持。现在,我们可以使用 shell 而不是使用 npm 类型。例如。

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Run Cypress",
      "type": "shell",
      "command": "cd tests/e2e/cypress/ && npm run cypress",
    }
}

总结

基于上面的例子,你可以在几分钟内配置好您的自定义开发工作流,并享受到运行脚本并直接在编辑器中查看结果的完全集成体验。

本文告诉大家如何使用VSCode Task改善日常工作,希望对大家有所帮助。对JavaScript感兴趣的同学可以看一下教程

JavaScript教程:https://www.w3cschool.cn/javascript/

JavaScript微课:https://www.w3cschool.cn/minicourse/play/jscourse

文章参考来源:www.toutiao.com/a6849220174004879884

带你了解什么是Python操作MySQL数据库

thbcm阅读(207)

写这篇文章主要是为了介绍Python操作MySQL数据库,并结合相应的实例带你更加深入了解。文中的代码实例很详细,对大家有一定的参考学习价值。

1.什么是pymysql?

PyMySQL是在Python3.x版本中用于连接MySQL服务器的一个库,Python2中使用mysqldbPyMySql遵循Python数据库API v2.0的规范,并包含了pure-Python MySQL客户端库。

2.安装PyMySQL

 $ pip install pymysql

3.MySQL数据库安装与配置

在用PyMySQL连接MySQL数据库之前,请确保MySQL数据库安装配置完成,具体如何安装与配置MySQL数据库,请参考 MySQL 安装MySQL 管理

4.1.连接数据库操作

import pymysql


# 数据库服务器名
HOSTNAME = 'node05'
# 数据库用户名
USER = 'root'
# 数据库名
DATABASE = 'cayman'
# 数据库密码
PASSWORD = 'Love88me'


# 打开数据库连接
conn = pymysql.connect(HOSTNAME, USER, PASSWORD, DATABASE)


# 使用cursor()方法创建一个游标对象
cursor = conn.cursor()


# 使用execute()方法执行SQL查询语句
cursor.execute("select VERSION()")


# 使用fetchone()查询单条数据
data = cursor.fetchone()
print(f"Database Version: {data}")


# 关闭数据库连接
conn.close()

4.2.创建表操作

import pymysql


# 设置数据库配置项
HOSTNAME = 'node05'
USERNAME = 'root'
PASSWORD = 'Love88me'
DATABASE = 'cayman'


# 打开数据库连接
db = pymysql.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE)


# 使用cursor对象创建一个流标对象
cursor = db.cursor()


# 使用execute()方法执行SQL, 如果表存在则删除
cursor.execute("DROP TABLE IF EXISTS employee")


# 使用预处理语句创建表
sql = """ CREATE TABLE employee(
    id bigint primary key auto_increment,
    user_name varchar(50) not null,
    age int,
    sex char(1),
    income float
)
"""


# 执行sql语句
cursor.execute(sql)


# 关闭数据库连接
db.close()

4.3.1.数据库插入单条语句

import pymysql


# 设置数据库配置项
HOSTNAME = 'node05'
USERNAME = 'root'
PASSWORD = 'Love88me'
DATABASE = 'cayman'


# 打开数据库连接
db = pymysql.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE)


# 使用cursor对象创建一个流标对象
cursor = db.cursor()


# SQL语句
sql = """
 insert into employee(user_name, age, sex, income) values ('风清扬', 64, '男', 22000);
"""


try:
    # 执行sql语句
    cursor.execute(sql)
    # 提交
    db.commit()
except:
    # 如果发生错误就回滚
    db.rollback()


# 关闭数据库连接
db.close()

4.3.2.数据库插入多条语句

import pymysql


# 设置数据库配置项
HOSTNAME = 'node05'
USERNAME = 'root'
PASSWORD = 'Love88me'
DATABASE = 'cayman'


# 打开数据库连接
db = pymysql.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE)


# 使用cursor对象创建一个流标对象
cursor = db.cursor()


# SQL语句
sql = " insert into employee(user_name, age, sex, income) values (%s, %s, %s, %s)"


data = (
    ('风清扬', 64, '男', 22000),
    ('令狐冲', 22, '男', 14000),
    ('任盈盈', 20, '男', 10000),
    ('东方不败', 32, '男', 18000),
    ('任我行', 56, '男', 17000),
    ('段誉', 33, '男', 19000),
    ('王语嫣', 26, '女', 9000),
    ('木婉清', 23, '女', 6000),
    ('乔峰', 38, '男', 23000),
    ('阿朱', 24, '女', 5000),
    ('阿紫', 22, '女', 5500),
    ('虚竹', 35, '男', 11000),
    ('梦姑', 25, '女', 6500),
    ('梅超风', 41, '女', 15000),
    ('陈玄风', 44, '男', 12000),
    ('杨过', 28, '男', 24000),
    ('小龙女', 38, '女', 15000),
    ('鸠摩智', 44, '男', 16000)
)


try:
    # 执行sql语句
    cursor.executemany(sql, data)
    # 提交
    db.commit()
except:
    # 如果发生错误就回滚
    db.rollback()


# 关闭数据库连接
db.close()

4.4.数据库查询

Python查询MySQL使用fetchone()获取单条数据,使用fetchall()方法获取多条数据。

  • fetchone(): 该方法获取下一个查询结果集。结果集是一个对象
  • fetchall(): 接收全部的返回结果行;
  • rowcount(): 这是一个只读属性,并返回执行execute()方法后影响的行数。

4.4.1.查询示例

查询employee表中income(工资)大于20000的所有数据

# 1.查询employee表中工资大于20000的员工信息
import pymysql


# 设置数据库配置项
HOSTNAME = 'node05'
USERNAME = 'root'
PASSWORD = 'Love88me'
DATABASE = 'cayman'


# 打开数据库连接
db = pymysql.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE)


# 使用cursor对象创建一个流标对象
cursor = db.cursor()


# 查询语句
sql = "select * from employee where income >'%d' "%(20000)


try:
    # 执行sql语句
    cursor.execute(sql)
    # 获取所有满足条件的列表
    ret = cursor.fetchall()
    # 遍历打印结果
    for row in ret:
        user_name = row[1]
        age = row[2]
        sex = row[3]
        income = row[4]
        print(f"员工: {user_name},年龄: {age}, 性别: {sex}, 工资: {income}")
except:
    print("无满足条件的数据或查询出错!!")


# 关闭数据库连接
db.close()

4.5.数据库更新操作

import pymysql


# 设置数据库配置项
HOSTNAME = 'node05'
USERNAME = 'root'
PASSWORD = 'Love88me'
DATABASE = 'cayman'


# 打开数据库连接
db = pymysql.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE)


# 使用cursor对象创建一个流标对象
cursor = db.cursor()


# 更新语句
sql = "update employee set income=income+income*0.1 where sex='%c'"%('女')


try:
    # 执行SQL语句
    cursor.execute(sql)
    # 提交
    db.commit()


except:
    # 发生错误时回滚
    db.rollback()


# 关闭数据库
db.close()

4.6.删除操作

import pymysql


# 设置数据库连接信息
HOSTNAME = 'node05'
USERNAME = 'root'
PASSWORD = 'Love88me'
DATABASE = 'cayman'


# 打开数据库连接
db = pymysql.connect(HOSTNAME, USERNAME, PASSWORD, DATABASE)


# 使用cursor()方法获取游标
cursor = db.cursor()


# 构建删除数据SQL语句
sql = "delete from employee where user_name = '%s'"%('鸠摩智')


try:
    # 执行sql语句
    cursor.execute(sql)
    # 提交
    db.commit()


except:
    # 发生异常时回滚
    db.rollback()


# 关闭数据库连接
db.close()

4.7 执行事务操作

在数据库操作中,事务机制可以保证数据的一致性。最基本的事务应当具备4个属性: 原子性、一致性、隔离性、和持久性。这四个属性被称作ACID特性。

  • 原子性(Atomicity): 一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
  • 一致性(Consistency): 事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
  • 隔离性(Isolation): 一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(Durability): 持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

Python DB API 2.0的事务提供了两个方法commitrollback。对于支持事务的数据库编程中,当流标建立时,就自动开启了一个隐形的数据库事务。

4.8 错误处理

DB API中定义了一些数据库操作的错误及异常,下表列出了这些错误和异常:

以上就是关于Python操作MySQL数据库的讲解了,希望对大家有所帮助,或许同学们也可以进入以下教程学习一下

python教程:https://www.w3cschool.cn/python/

python3基础微课:https://www.w3cschool.cn/minicourse/play/python3course

关于Linux重定向的用法详解

thbcm阅读(177)

我们平时都会需要复制黏贴数据,假如打开文件进行复制黏贴操作,那么就有的繁琐,那么能不能省掉这些步骤呢?

当然能,重定向是一种高效的方法,它无需大量的鼠标与键盘操作就可以完成数据的转移。重定向可以分为输入重定向以及输出重定向这两种类型。由于所有程序都有输入或者输出,因此输入和输出的重定向是任何编程语言或脚本语言都自带的功能。

每当你与计算机交互时,重定向就必然会发生。学会使用重定向,不仅可以让你与计算机更好地交互,还可以提高你的工作效率。接下来给大家讲解一下 Linux 系统中重定向的常见用法:

Linux 中的数据流

谈到 Linux 的重定向,就不得不提以下这3种数据流:

  • 输入信息会从 stdin 中读取(标准输入,通常是键盘或鼠标)。
  • 输出信息会被输出到 stdout (标准输出,一个文本文件或者数据流)。
  • 错误信息会被输出到 stderr

了解了这些数据流的存在,在你使用 Shell 时,你就可以更好地控制数据的流向了。

Linux 系统中,标准输入,标准输出以及标准错误都作为文件存在。 你可以在 /dev 目录下看到它们:

$ ls /dev/std* /dev/stderr /dev/stdin /dev/stdout

重定向输出

Linux 系统中,使用 > 字符表示重定向输出。例如,将 ls命令的输出重定向到一个文件中:

$ ls > list.txt

执行以上命令后,屏幕上并不会显示 ls 命令的输出信息,因为输出信息已经被重定向至 list.txt 文件中了。

除此之外,重定向还有许多用途,它还可以用于复制文件的内容,而且不限于复制文本文件,二进制文件也可以复制:

$ cat image.png > picture.png

如果你想要将一个文件的内容复制到另一个文件的末尾,你只需将> 字符换成 >> 字符串即可,像这样:

$ cat lxlinux >> alvin

重定向输入

与重定向输出相反,重定向输入使用的是 <字符。

输入重定向可以将输入信息重定向至命令中作为参数使用。该功能可能比较少用,但是,当命令需要一个参数列表时,而这些参数都存在一个文件中,然后你想快速地将它们从文件中复制粘贴到终端,这时这个功能就能派上用场了。

例如,package.list 里记录了你需要安装的包的列表,而你想要快速地安装所有的包,只需执行以下这一条命令,就能一次性安装 package.list 里的所有包:

$ sudo dnf install $(<package.list)

输入重定向的常见用法是 Here-document (简称 Here-doc) 以及 Here-string

Here-doc 将输入的文本块重定向至标准输入流,直至遇到特殊的文件结束标记符为止(文件结束标记符可以是任意的唯一的字符串,但大部分人都默认使用 EOF)。

你可以尝试在终端输入以下命令(直到第二个 EOF 字符串结束):

$ cat << EOF

alvin
lxlinux.net
EOF

预期的输出应该是这样的:

alvin lxlinux.net

Here-docBash 脚本编写者们将多行文本转储到文件或屏幕上的常用技巧。

Here-stringHere-doc 相似,但是它只有一个字符串,或者几个被引号括起来的字符串:

$ cat <<< alvin alvin $ cat <<< “alvin lxlinux.net” alvin lxlinux.net

重定向错误信息

错误信息默认会进入叫 stderr 的流,使用 2> 可以对其进行重定向。例如,将错误信息重定向到名为 output.log 的文件中:

$ ls /nope 2> output.log

重定向数据至 /dev/null

就像标准输入、标准输出以及标准错误一样,在 Linux 文件系统中,空,也存在一个文件与之对应,它叫做 null ,放在 /dev 目录下。为了方便读,人们经常省略斜杠,直接把他读作dev null

/dev/null 并不保存数据,被写入 /dev/null 的数据最终都会丢失,就像被丢进虚空中一样。因此,你可以使用重定向将不需要的数据输送到 /dev/null 。例如,find命令的输出往往很冗长,而且在搜索文件时还经常会报告权限冲突的错误,像这样:

$ find ~ -type f /home/seth/actual.file find: /home/seth/foggy': Permission denied find:/home/seth/groggy’: Permission denied find: `/home/seth/soggy’: Permission denied /home/seth/zzz.file

这时,你就可以将错误信息重定向到 /dev/null ,以过滤掉不必要的信息,像这样:

$ find ~ -type f 2> /dev/null /home/seth/actual.file /home/seth/zzz.file

善用重定向

Bash 中,重定向是转移数据的一种高效方法。你可能并不会总是使用重定向,但是学会如何使用重定向,在你需要的时候可以让你省掉许多不必要的复制粘贴操作,因此也节省了许多操作鼠标与键盘的时间。请不要执着于复制粘贴了,使用重定向可以提高你的工作效率,难道,它不香吗?

以上就是关于Linux 系统中的重定向的讲解了,希望对大家的工作有所帮助,想学习更多Linux知识的同学可以看一下教程:

Linux教程:https://www.w3cschool.cn/linux/

Linux微课:https://www.w3cschool.cn/minicourse/play/linuxcourse

Linux就该这么学:https://www.w3cschool.cn/linuxprobe/

文章参考来源:www.toutiao.com/a6854146447089074695/

C++空指针可以用nullptr代替NULL

thbcm阅读(193)

首先了解一下什么是空指针,空指针就是void*

C/C++中,为了避免野指针(即指针没有指向任何地址)的出现,声明一个指针后,最好马上对其进行初始化。

如果暂时不明确指针指向哪个变量,则可以赋予NULL,如:

int* p = NULL;

除了NULL之外,C++11新标准引入了nullptr来表示一个空指针。

nullptr 既不是整型类型,也不是指针类型,nullptr 的类型是 std::nullptr_t,能转换成任意的指针类型。

为什么建议使用nullptr代替NULL呢?

这是因为在C++中,NULL是被定义为0的常量,当遇到函数重载时,就会出现问题。

比如有下面两个函数时:

  • void foo(int n)
  • void foo(char* s)

函数重载:C++允许在同一作用域中声明多个类似的同名函数,这些同名函数的形参列表(参数个数,类型,顺序)必须不同。

#include <iostream>
using namespace std;


void foo(int n) {
    cout << "foo(int n)" << endl;
}


void foo(char* s) {
    cout << "foo(char* s)" << endl;
}


int main()
{
    foo(NULL);
    return 0;
}

编译上述代码,结果如下图所示,编译器提示有两个函数都可能匹配,产生二义性。

而用nullptr,编译器则会选择 foo(char* s)的函数,因为nullptr不是整数类型。

#include <iostream>
using namespace std;


void foo(int n) {
    cout << "foo(int n)" << endl;
}


void foo(char* s) {
    cout << "foo(char* s)" << endl;
}


int main()
{
    foo(nullptr);
    return 0;
}

运行结果如下图所示:

因此,当需要使用空指针时,优先使用nullptr,而非NULL

以上就是关于c++空指针为什么要用nullptr代替NULL的原因了,希望对大家有所帮助,然后想学习更多C++知识的同学可以看一下教程

C++教程:https://www.w3cschool.cn/cpp/

C++微课:https://www.w3cschool.cn/minicourse/play/cppminicourse

文章参考来源:www.toutiao.com/a6854549524803748356/

Vue 3 异步组件的重新“定义”

thbcm阅读(197)

首先我们来看一下有关于异步组件的变化:

  • 新增了一个方法:defineAsyncComponent,用来显式地定义异步组件
  • component 选项改名:loader
  • Loader 函数不再接受 resolvereject 作为参数,并且必须返回一个 Promise

曾经的异步组件

Vue 2.x 中,定义一个异步组件还是很方便的:

const asyncPage = () => import(‘./HugePageComponent.vue’);

如果需要一些高级的用法(我赌五毛钱你不知道这个用法):

import { ErrorComponent, LoadingComponent } from ‘xxx’; const asyncPage = { component: () => import(‘./HugePageComponent’), delay: 1000, timeout: 3000, error: ErrorComponent, loading: LoadingComponent, }

即将到来的 Vue 3 异步组件

因为在 Vue 3 中函数式组件均有普通函数来定义,所以异步组件需要通过 defineAsyncComponent 来进行显式地定义。

import { defineAsyncComponent } from ‘vue’; import { ErrorComponent, LoadingComponent } from ‘xxx’;


// 常规用法
const asyncPage = defineAsyncComponent(() => import('./HugePageComponent'));


// 高级用法
const asyncPageWithOptions = defineAsyncComponent({
  // 这里之前是 component,现在改叫 loader 了 
  loader: () => import('./HugePageComponent'),
  delay: 1000,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent,
});

另外,和 Vue 2.x 不同的是,loader 函数不再提供 resolvereject 作为默认参数了,而且必须返回一个 Promise

// 2.x 版本 const oldAsyncComponent = (resolve, reject) => { // }


// 3.x 版本
const newAsyncComponent = defineAsyncComponent(
 () => {
    return new Promise((resolve, reject) => {
      /* ... */
    });
  }
);

以上就是Vue 3 关于异步组件的改变,对Vue感兴趣的同学可以看一下教程

Vue 1教程:https://www.w3cschool.cn/vuejs/

Vue 2教程:https://www.w3cschool.cn/vuejs2/

Vue 2.x 微课:https://www.w3cschool.cn/minicourse/play/vuecourse

文章参考来源:www.toutiao.com/a6853814142684365320/

充满了汗水和泪水的编程经验,初学者请注意

thbcm阅读(186)

作为一名程序员,在编程中,难免会遇到很多坑。小编有过几年的编程经历,这中间为自己为别人挖过很多坑,也踩过别人的坑,帮别人填过坑。作为一名过来人,我把自己踩坑的经验总结一下,让大家参考一下,或许能避免一些坑。

1.任何修改都要经过测试才可以上线

小编有次在投产上线前发现自己的代码有bug,由于时间紧迫,小编改完后阅读了下代码自认为没有问题了,自测都没有进行,就把代码提交上线了。

结果第二天用户使用这个功能时,直接报错了,只得当天晚上重启服务器放上经过测试的代码。

此事被大领导通报批评,连累项目经理一起挨批。

2.sql防注入是最基本的常识

小编刚开始做项目的时候,看到项目组中有把入参拼接在sql中,没有采用预编译的方式输入参数,小编也跟着这样写。

而这些接口都是通过外网手机app端来调用的,危险性瞬间提高。

部门的安全组及时扫描识别出这些问题,小编花了整整一个元旦的假期才把这些sql拼接参数的代码换成预编译的方式,还改错了一个接口,还好修复完后没有大碍。

sql注入是一件极其危险的事情,使用预编译的方式避免sql注入是最有效的方式之一。当然,除了sql注入,还有命令注入等等注入。

3.编程的关键在于解耦以及可读性

小编之前的老板教小编,好的代码一定要具有良好的可读性,可读性是可维护性的基础。

小编写代码的时候就琢磨,这个类,这个方法,这个变量起什么名字好呢?好的代码是具有自解释的能力。

小编在维护之前同事的代码时,发现有的同事的代码写得又臭又长,变量有时是a1,a2,a3之类的。明明是个新增方法,偏偏用get开头;有的同事用魔鬼数字,看得小编莫名其妙。

解耦性呢,就是我做我该做的事情,你做你该做的事情,互不干涉内政,各自应对变化。

比如说大家继承了一个类或者实现了一个接口,就各自做好自己本分的工作就好了。

又比如说一个复杂的逻辑,可以分拆成多个子逻辑,每个子逻辑就解耦开来,修改一个方法,不会影响另一个方法的使用,方法的复杂度也降低了。

4.尽量不要重复造轮子

有的类或者jar包已经被广泛应用,没有什么问题了,自己有空研究就好,没有必要再写一个了。

之前小编做一个导入的功能时,由于要入库的数据很大,需要对集合分割分批导入。

小编就写了一个分割集合的方法,经项目另外一个同事的提醒,发现系统引入的开源jar包中已经有这个方法了,直接导包使用就行了。

集合的分组,过滤,listmap,list对象提取属性等使用java 8 的项目都可以通过java8的流来操作。

5.数据库建表要尽量遵循数据库表的范式

小编的项目组发现很多表都建立了不必要的冗余字段,比如名称这些。

当用户修改了基表的数据时,业务表的名称数据又没有修改过来,而查询的时候却不是关联基表去查询名称字段的,导致用户两边看到的数据不一致。

维护这些数据和修改查询功能花费了小编大量的时间。

6.尽量不要答应业务直接在后台数据库导数入库

数据库导入数绕过了代码逻辑,没有经过代码逻辑的拦截和业务规则的校验,有可能导致不合法的数据入库,甚至影响正常的业务流程。

而且导入的数据往往数量巨大,更加重的之后的维护成本。之前小编做的功能也导入过大量历史存量数据,结果这些数据很多有问题。

导完这些数据后,用户发现对现有的使用造成了影响,不得不一笔笔向业务确认,重新刷数,真是心累。

那么有想了解SQL数据库的同学,可以看一下教程

SQL教程:https://www.w3cschool.cn/sql/

MySQL微课:https://www.w3cschool.cn/minicourse/play/mysqlcourse

带你了解C语言自动变量的几种类型

thbcm阅读(182)

首先先了解一下什么是自动变量,属于自动存储类别的变量具有自动存储期、块作用域且无链接。默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。一般情况下,不作专门说明的局部变量,均是自动变量。

关键字auto

为了更清楚地表达你的意图(例如,为了表明有意覆盖一个外部变量定义,或者强调不要把该变量改为其他存储类别),可以显式使用关键字auto,如下所示:

int main(void)
{
  auto int plox;

关键字auto是存储类别说明符(storage-class specifier)。auto关键字在C++中的用法完全不同,如果编写C/C++兼容的程序,最好不要使用auto作为存储类别说明符。

块作用域无链接意味着只有在变量定义所在的块中才能通过变量名访问该变量(当然,参数用于传递变量的值和地址给另一个函数,但是这是间接的方法)。另一个函数可以使用同名变量,但是该变量是存储在不同内存位置上的另一个变量。

变量具有自动存储期意味着,程序在进入该变量声明所在的块时变量存在,程序在退出该块时变量消失。原来该变量占用的内存位置现在可做他用。

嵌套快的情况

接下来分析一下嵌套块的情况。块中声明的变量仅限于该块及其包含的块使用。

int loop(int n)
{
     int m;          // m in scope
     scanf("%d", &m);
     {
          int i;    // both m and i in scope
          for (i = m; i < n; i++)
               puts("i is local to a sub-blockn");
     }
     return m;     // m in scope, i gone
}

在上面的代码中,i仅在内层块中可见。如果在内层块的前面或后面使用i,编译器会报错。通常,在设计程序时用不到这个特性。然而,如果这个变量仅供该块使用,那么在块中就近定义该变量也很方便。这样,可以在靠近使用变量的地方记录其含义。另外,这样的变量只有在使用时才占用内存。变量nm分别定义在函数头和外层块中,它们的作用域是整个函数,而且在调用函数到函数结束期间都一直存在。

如果内层块中声明的变量与外层块中的变量同名会怎样?内层块会隐藏外层块的定义。但是离开内层块后,外层块变量的作用域又回到了原来的作用域。程序hiding.c演示了这一过程。

// hiding.c -- variables in blocks
#include <stdio.h>
int main()
{
    int x = 30;      // original x


    printf("x in outer block: %d at %pn", x, &x);
    {
        int x = 77;  // new x, hides first x
        printf("x in inner block: %d at %pn", x, &x);
    }
    printf("x in outer block: %d at %pn", x, &x);
    while (x++ < 33) // original x
    {
        int x = 100; // new x, hides first x
        x++;
        printf("x in while loop: %d at %pn", x, &x);
    }
    printf("x in outer block: %d at %pn", x, &x);


    return 0;
}

下面是该程序的输出:

x in outer block: 30 at 0x7fff5fbff8c8
x in inner block: 77 at 0x7fff5fbff8c4
x in outer block: 30 at 0x7fff5fbff8c8
x in while loop: 101 at 0x7fff5fbff8c0
x in while loop: 101 at 0x7fff5fbff8c0
x in while loop: 101 at 0x7fff5fbff8c0
x in outer block: 34 at 0x7fff5fbff8c8

首先,程序创建了变量x并初始化为30,如第1条printf()语句所示。然后,定义了一个新的变量x,并设置为77,如第2条printf()语句所示。根据显示的地址可知,新变量隐藏了原始的x。第3条printf()语句位于第1个内层块后面,显示的是原始的x的值,这说明原始的x既没有消失也不曾改变。也许该程序最难懂的是while循环

while循环的测试条件中使用的是原始的x

while(x++ < 33)

在该循环中,程序创建了第3个x变量,该变量只定义在while循环中。所以,当执行到循环体中的x++时,递增为101的是新的x,然后printf()语句显示了该值。每轮迭代结束,新的x变量就消失。然后循环的测试条件使用并递增原始的x,再次进入循环体,再次创建新的x。在该例中,这个x被创建和销毁了3次。注意,该循环必须在测试条件中递增x,因为如果在循环体中递增x,那么递增的是循环体中创建的x,而非测试条件中使用的原始x

我们使用的编译器在创建while循环体中的x时,并未复用内层块中x占用的内存,但是有些编译器会这样做。

该程序示例的用意不是鼓励读者要编写类似的代码(根据C的命名规则,要想出别的变量名并不难),而是为了解释在内层块中定义变量的具体情况。

没有花括号的块

前面提到一个C99特性:作为循环或if语句的一部分,即使不使用花括号({}),也是一个块。更完整地说,整个循环是它所在块的子块(sub-block),循环体是整个循环块的子块。与此类似,if语句是一个块,与其相关联的子语句是if语句的子块。这些规则会影响到声明的变量和这些变量的作用域。程序forc99.c演示了for循环中该特性的用法。

// forc99.c -- new C99 block rules
#include <stdio.h>
int main()
{
    int n = 8;


    printf("   Initially, n = %d at %pn", n, &n);
    for (int n = 1; n < 3; n++)
        printf("      loop 1: n = %d at %pn", n, &n);
    printf("After loop 1, n = %d at %pn", n, &n);
    for (int n = 1; n < 3; n++)
    {
        printf(" loop 2 index n = %d at %pn", n, &n);
        int n = 6;
        printf("      loop 2: n = %d at %pn", n, &n);
        n++;
    }
    printf("After loop 2, n = %d at %pn", n, &n);


    return 0;
}

假设编译器支持C语言的这个新特性,该程序的输出如下:

Initially, n = 8 at 0x7fff5fbff8c8
loop 1: n = 1 at 0x7fff5fbff8c4
loop 1: n = 2 at 0x7fff5fbff8c4
After loop 1, n = 8 at 0x7fff5fbff8c8
loop 2 index n = 1 at 0x7fff5fbff8c0
loop 2: n = 6 at 0x7fff5fbff8bc
loop 2 index n = 2 at 0x7fff5fbff8c0
loop 2: n = 6 at 0x7fff5fbff8bc
After loop 2, n = 8 at 0x7fff5fbff8c8

需要注意的一点:

支持C99C11有些编译器并不支持C99/C11的这些作用域规则(Microsoft Visual Studio 2012就是其中之一)。有些编译会提供激活这些规则的选项。例如,撰写本书时,gcc默认支持了C99的许多特性,但是要用–std=c99选项激活程序清单12.2中使用的特性:

gcc –std=c99 forc99.c

与此类似,gccclang都要使用–std=c1x-std=c11选项,才支持C11特性

程序forc99.c第1个for循环头中声明的n,其作用域作用至循环末尾,而且隐藏了原始的n。但是,离开循环后,原始的n又起作用了。

第2个for循环头中声明的n作为循环的索引,隐藏了原始的n。然后,在循环体中又声明了一个n,隐藏了索引n。结束一轮迭代后,声明在循环体中的n消失,循环头使用索引n进行测试。当整个循环结束时,原始的n又起作用了。再次提醒读者注意,没必要在程序中使用相同的变量名。如果用了,各变量的情况如上所述。

自动变量的初始化

自动变量不会初始化,除非显式初始化它。考虑下面的声明:

int main(void)
{
  int repid;
  int tents = 5;

tents变量被初始化为5,但是repid变量的值是之前占用分配给repid的空间中的任意值(如果有的话),别指望这个值是0。可以用非常量表达式(non-constantexpression)初始化自动变量,前提是所用的变量已在前面定义过:

int main(void)
{
  int repid;
  int tents = 5;

以上就是关于C语言的几种自动变量的说明了,有兴趣的同学可以看一下C语言的相关教程

C教程:https://www.w3cschool.cn/c/

你知道C语言外部链接的静态变量有哪几种吗?

thbcm阅读(171)

上文说到了C语言自动变量的几种类型,这里就说一下c语言外部链接的静态变量的类型。

外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别有时称为外部存储类别(external storage class),属于该类别的变量称为外部变量(external variable)。把变量的定义性声明(defining declaration)放在所有函数的外面便创建了外部变量。

当然,为了指出该函数使用了外部变量,可以在函数中用关键字extern再次声明。如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须用extern在该文件中声明该变量。如下所示:

int Errupt;           /* externally defined variable    */
double Up[100];       /* externally defined array       */
extern char Coal;     /* mandatory declaration if       */
                      /* Coal defined in another file   */
void next(void);
int main(void)
{
  extern int Errupt;  /* optional declaration           */


  extern double Up[]; /* optional declaration           */
  ...
}
void next(void)
{
  ...
}

注意,在main()中声明Up数组时(这是可选的声明)不用指明数组大小,因为第1次声明已经提供了数组大小信息。main()中的两条extern声明完全可以省略,因为外部变量具有文件作用域,所以ErruptUp从声明处到文件结尾都可见。它们出现在那里,仅为了说明main()函数要使用这两个变量。如果省略掉函数中的extern关键字,相当于创建了一个自动变量。去掉下面声明中的extern

extern int Errupt;

便成为:

int Errupt;

这使得编译器在main()中创建了一个名为Errupt的自动变量。它是一个独立的局部变量,与原来的外部变量Errupt不同。该局部变量仅main()中可见,但是外部变量Errupt对于该文件的其他函数(如next())也可见。简而言之,在执行块中的语句时,块作用域中的变量将“隐藏”文件作用域中的同名变量。如果不得已要使用与外部变量同名的局部变量,可以在局部变量的声明中使用auto存储类别说明符明确表达这种意图。外部变量具有静态存储期。因此,无论程序执行到main()next()还是其他函数,数组Up及其值都一直存在。下面3个示例演示了外部和自动变量的一些使用情况。示例1中有一个外部变量Hocus。该变量对main()magic()均可见。

/* Example 1 */
int Hocus;
int magic();
int main(void)
{
   extern int Hocus;  // Hocus declared external
   ...
}
int magic()
{
   extern int Hocus;  // same Hocus as above
   ...
}

示例2中有一个外部变量Hocus,对两个函数均可见。这次,在默认情况下对magic()可见。

/* Example 2 */
int Hocus;
int magic();
int main(void)
{
   extern int Hocus;  // Hocus declared external
   ...
}
int magic()
{
                      // Hocus not declared but is known
   ...
}

在示例3中,创建了4个独立的变量。main()中的Hocus变量默认是自动变量,属于main()私有。magic()中的Hocus变量被显式声明为自动,只有magic()可用。外部变量Hocusmain()magic()均不可见,但是对该文件中未创建局部Hocus变量的其他函数可见。最后,Pocus是外部变量,magic()可见,但是main()不可见,因为Pocus被声明在main()后面。

/* Example 3 */
int Hocus;
int magic();
int main(void)
{
  int Hocus;        // Hocus declared, is auto by default
   ...
}
int Pocus;
int magic()
{
   auto int Hocus;  // local Hocus declared automatic
   ...
}

这3个示例演示了外部变量的作用域是:从声明处到文件结尾。除此之外,还说明了外部变量的生命期。外部变量HocusPocus在程序运行中一直存在,因为它们不受限于任何函数,不会在某个函数返回后就消失。

初始化外部变量

外部变量和自动变量类似,也可以被显式初始化。与自动变量不同的是,如果未初始化外部变量,它们会被自动初始化为0。这一原则也适用于外部定义的数组元素。与自动变量的情况不同,只能使用常量表达式初始化文件作用域变量:

int x = 10;              // ok, 10 is constant
int y = 3 + 20;          // ok, a constant expression
size_t z = sizeof(int);  // ok, a constant expression
int x2 = 2 * x;          // not ok, x is a variable

(只要不是变长数组,sizeof表达式可被视为常量表达式。)

使用外部变量

下面来看一个使用外部变量的示例。假设有两个函数main()critic(),它们都要访问变量units。可以把units声明在这两个函数的上面,如程序清单12.4所示(注意:该例的目的是演示外部变量的工作原理,并非它的典型用法)。

/* global.c  -- uses an external variable */
#include <stdio.h>
int units = 0;         /* an external variable      */
void critic(void);
int main(void)
{
    extern int units;  /* an optional redeclaration */


    printf("How many pounds to a firkin of butter?n");
    scanf("%d", &units);
    while ( units != 56)
        critic();
    printf("You must have looked it up!n");


    return 0;
}


void critic(void)
{
    /* optional redeclaration omitted */
    printf("No luck, my friend. Try again.n");
    scanf("%d", &units);
}

下面是该程序的输出示例:

How many pounds to a firkin of butter?
14
No luck, my friend. Try again.
56
You must have looked it up!
(We did.)

注意,critic()是如何读取units的第2个值的。当while循环结束时,main()也知道units的新值。所以main()函数和critic()都可以通过标识符units访问相同的变量。用C的术语来描述是,units具有文件作用域、外部链接和静态存储期。

units定义在所有函数定义外面(即外部),units便是一个外部变量,对units定义下面的所有函数均可见。因此,critics()可以直接使用units变量。

类似地,main()也可直接访问units。但是,main()中确实有如下声明:

extern int units;

本例中,以上声明主要是为了指出该函数要使用这个外部变量。存储类别说明符extern告诉编译器,该函数中任何使用units的地方都引用同一个定义在函数外部的变量。再次强调,main()critic()使用的都是外部定义的units

外部名称

C99C11标准都要求编译器识别局部标识符的前63个字符和外部标识符的前31个字符。这修订了以前的标准,即编译器识别局部标识符前31个字符和外部标识符前6个字符。你所用的编译器可能还执行以前的规则。外部变量名比局部变量名的规则严格,是因为外部变量名还要遵循局部环境规则,所受的限制更多。

定义和声明

下面进一步介绍定义变量和声明变量的区别。考虑下面的例子:

int tern = 1;            /* tern defined                 */
main()
{
     external int tern;  /* use a tern defined elsewhere */

这里,tern被声明了两次。第1次声明为变量预留了存储空间,该声明构成了变量的定义。第2次声明只告诉编译器使用之前已创建的tern变量,所以这不是定义。第1次声明被称为定义式声明(defining declaration),第2次声明被称为引用式声明(referencing declaration)。关键字extern表明该声明不是定义,因为它指示编译器去别处查询其定义。

假设这样写:

extern int tern;
int main(void)
{

编译器会假设tern实际的定义在该程序的别处,也许在别的文件中。该声明并不会引起分配存储空间。因此,不要用关键字extern创建外部定义,只用它来引用现有的外部定义。

外部变量只能初始化一次,且必须在定义该变量时进行。假设有下面的代码:

// file one.c
char permis = 'N';
...
// file two.c
extern char permis = 'Y';   /* error */

file_two中的声明是错误的,因为file_one.c中的定义式声明已经创建并初始化了permis

以上就是关于C语言外部链接的静态变量类型的说明,对C语言有兴趣的同学可以看一下教程

C教程:https://www.w3cschool.cn/c/

Linux系统如何使用Vim读写远程文件

thbcm阅读(170)

这篇文章我们将说到Linux系统Vim的使用——读写远程文件。在实现这个功能之前,我们要先安装一个叫 netrw.vim的插件。

插件 netrw 是什么?

netrw(面向网络的读写和浏览 Network oriented reading, writing and browsing)插件不仅支持本地和远程终端跨网络编辑、读写文件,还能支持浏览本地和远程终端目录。需要了解这个插件的更多细节信息,在Vim会话输入 help netrw 即可查看。

下面让我们来看看怎么用 Vim 从本地系统读写存储在远程 Linux 系统上的文件。

使用 Linux 系统上的 Vim 读写远程文件

读写远程文件的方法几乎和编辑远程文件一样,需要从本地系统读取一个远程文件,我们可以简单的使用下面这个命令来打开它:

$ vim scp://cirdan@192.168.225.22/info.txt

输入 q 退出文件。

要从本地系统写一个远程文件,也是先用上述命令打开文件,之后再按 i 进入插入模式接着就可以往文件里写入了。等写完需要写入文件的内容之后,按 ESC 键退出插入模式,然后输入 wq 保存并退出。

命令背后的运行过程实际上是用 scp 命令将远程文件拷贝到本地系统的 /tmp 目录下,然后再打开文件编辑的。在你编辑完后,scp 命令再次将本地已编辑文件拷贝回远程系统。

要从本地查看远程文件内容是否真的改动过用这一条命令:

$ ssh cirdan@192.168.225.22 cat info.txt

请注意,如果你要用远程终端目录的绝对路径,应像如下所示命令一样使用双斜杠:

$ vim scp://cirdan@192.168.225.22//home/cirdan/Documents/info.txt

如果你已经因为安全原因改变了SSH端口,则应当显式地说明SSH端口号,如下所示:

$ vim scp://cirdan@192.168.225.22:2200/info.txt

这里端口 2200 就是我们自定义的端口号,大家可以根据具体情况使用自己的 ssh 端口号替代上述命令中的2200。

如果你没有 ssh/scp 的通道,也可以用其他协议来替代,如下所示:

$ vim ftp://user@remotesystem/path/to/file

在Vim会话里读写远程文件

如果你已经进入了一个 Vim 会话,则可以使用 Nread(NetRead)和 Nwrite(NetWrite)命令来读写远程文件。

假如,我们现在使用如下命令打开本地系统的Vim 编辑器:

$ vim

接着你就进入了 Vim 会话,要在本地新缓存中的 Vim 会话中读一个远程文件,只需要运行如下命令:

:e scp://cirdan@192.168.225.22/info.txt

除此之外,还可以用 Nread 命令如下所示:

:Nread scp://cirdan@192.168.225.22/info.txt

或者,这样输入:

:Nread “scp://cirdan@192.168.225.22/info.txt”

想要知道这个命令的详细信息,请在 Vim 会话中输入如下命令:

:Nread ?

读完说明文件后,输入 :q退出文件即可。

与之相似,要写入远程文件也应先使用如下命令:

:e scp://cirdan@192.168.225.22/info.txt

i 键进入插入模式就可以写入和修改文件了。

你也可以用 :w 创建并写文件,但是这个命令只能创建一个新的空文件:

:w scp://cirdan@192.168.225.22/info.txt

写完之后,按 ESC 键退出编辑,然后输入:wq保存并退出文件。

除此之外,还可以用 Nwrite 命令创建并写入文件,其用法示例如下:

:Nwrite scp://cirdan@192.168.225.22/info.txt

关于 Nwrite 命令的详细信息,在Vim会话中输入如下信息即可:

:Nwrite ?

以上就是Linux系统中Vim的使用技巧了,希望对大家有所帮助,对Linux感兴趣的同学可以看一下教程

Linux教程:https://www.w3cschool.cn/linux/

Linux微课:https://www.w3cschool.cn/minicourse/play/linuxcourse

Linux就该这么学:https://www.w3cschool.cn/linuxprobe/

联系我们