ES数据库操作入门总结「建议收藏」

ES数据库操作入门总结「建议收藏」elasticsearch总的来说应该算是一个搜索引擎,公司使用一般是作为日志结果查询。json文档格式,倒排索引的快速以及分布式的特性,使得es可以在大量数据中快速查询到结果。windows安装和配置可参考官方网址。https://www.elastic.co/guide/en/elasticsearch/reference/current/zip-windows.html倒排查询可参考这个知乎回答https://zhuanlan.zhihu.com/p/62892586可以使用浏览器的U

大家好,又见面了,我是你们的朋友全栈君。

elasticsearch总的来说应该算是一个搜索引擎,公司使用一般是作为日志结果查询。
json文档格式,倒排索引的快速以及分布式的特性,使得es可以在大量数据中快速查询到结果。

windows安装和配置可参考官方网址。

https://www.elastic.co/guide/en/elasticsearch/reference/current/zip-windows.html

倒排查询可参考这个知乎回答

https://zhuanlan.zhihu.com/p/62892586

可以使用浏览器的URL栏输入或者使用postman来输入类似URL形式的代码来操作es,即输入主机名:端口号/索引名/类名/id/_search这种(电脑默认get方法,不用输)。如果要操作可能还是用postman比较好,因为可以很方便的创建json文本数据。不过可以的话,推荐使用kibana这种软件操作。但是由于es比较需要使用大量数据来操作搜索进行练习,因此可以的话,最好用比较方便的软件创建大量测试数据操作。

ES的安装

es的安装非常简单,可以直接在自身主机上安装,并开始使用。不需要集群也可以创建elastic节点。
操作的格式如下

GET /索引名/类名/id/方法名 { 
   
						条件
						}

索引名,类名,id可省略,默认为在所有索引中操作。
经过一段时间学习与运用发现es的操作一般只涉及查询,这也符合作为搜索引擎的特性。因此以下都只谈查询。
查询一般使用方法**_search**,下接query如下

GET /_search
{ 
   
	"query" : { 
   
		"match_all":{ 
   }
		}
}

全索引查询相关度前十的结果。

查询出来的结果包括许多部分。需要关注的部分以这个为例

{ 
   
  "took" : 6479,
  "timed_out" : false,
  "num_reduce_phases" : 9,
  "_shards" : { 
   
    "total" : 4260,
    "successful" : 4260,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : { 
   
    "total" : { 
   
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : 1.0,
    "hits" : [
      { 
   
        "_index" : ".data-frame-internal-2",
        "_type" : "_doc",
        "_id" : "data_frame_transform_config-test",
        "_score" : 1.0,
        "_source" : { 
   
          "id" : "test",
          "source" : { 
   
            "index" : [
              "logstash-applog*"
            ],
            "query" : { 
   
              "match_all" : { 
    }
            }
          },
 以下省略一大堆

took:表示你查询耗时,默认是ms,
timed_out: 表示你是否查询超过规定的时间,没超过显示false
shards下是一些分片信息,像这个就是查了4260个分片全都成功查询,没有跳过或者失败的。
hits:第一个下的total指的是查到的总的信息,value就是匹配的结果有10000个(其实好像是因为默认只允许最多查10000个),relation种的gte就是指value这个数据不准确,准确是eq。看下面这个就是eq

{ 
   
    "took": 0,
    "timed_out": false,
    "_shards": { 
   
        "total": 0,
        "successful": 0,
        "skipped": 0,
        "failed": 0
    },
    "hits": { 
   
        "total": { 
   
            "value": 0,
            "relation": "eq"
        },
        "max_score": 0.0,
        "hits": []
    }
}

max_score就是最高的相关性得分,就是和查询条件的匹配度,我这里是从0到1,数字越大匹配度越高,默认是查询结果按照相关性得分从高到低排序。

第二个hits,里面就放的是我查询到的结果。这个一般就包含五个部分,从上到下分别
_index:索引名
_type:类名
_id:id名
_score:相关性得分
_source:这里面我们就能看到我们想要的json数据了。

组合查询

其实指的就是bool查询,写法如下

GET /_search
{ 
   
	"query" : { 
   
		"bool" : { 
   
			"must" : { 
   
				"match" : { 
   
					"key" : "value"
					}
				}
			}
		}
	}

上面这个就是在所有索引库查key对应的值和value匹配的所有id表

和must同级的有must_not、should、和filter前面三个涉及相关性得分,后一个只起到过滤作用,不会计算相关性得分。前面两个起到过滤加计分作用,should在没must时候作用和must差不多,有must的话就只起到计分的作用,即不满足条件的也不会被过滤掉。

match同级 的我一般用的就三个,一个是term,要求精确值匹配,即value必须等于value而不是value able里面含有value。还有一个是terms,就是key必须等于value数组或队列(termsQuery可以放队列)中的一个值。还有一个就是range,一个范围内取值,这个看下面例子,gte就是比这个值大,lt就是比这个小。这里的price就是”key“,后面的就是value

GET /my_store/products/_search
{ 
   
    "query" : { 
   
            "filter" : { 
   
                "range" : { 
   
                    "price" : { 
   
                        "gte" : 20,
                        "lt"  : 40
                    }
                }            
        }
    }
}

value可以添加boot权重属性来影响相关性的得分比如这个分值越大权重越高。

},
            "should": [
                { 
    "match": { 
   
                    "content": { 
   
                        "query": "Elasticsearch",
                        "boost": 3 
                    }

其他的就多和自定义过滤器,分析器有关了。此外7.x的文档添加了许多有用属性,并且string这个映射类型已经被抛弃了,现在用的是text和keyword表示字符串类型。

聚合

聚合其实指的就是group by,即,将查询出的数据以某种方式分支,从而得到想要的度量值。我们先看这个例子
color这个属性来分组,这个colors其实就是给查询结果取了个名字,你可以随便取

GET /cars/transactions/_search
{ 
   
   "size" : 0,
   "aggs": { 
   
      "colors": { 
   
         "terms": { 
   
            "field": "color"
         }
      }
   }
}

查出来像这个

。。。这里省略的有hits部分还有上面的shards部分等等,这个例子因为size为0因此第二个hits里面是空的,就是一个[]
"aggregations": { 
   
      "colors": { 
   
         "buckets": [
            { 
   
               "key": "red",
               "doc_count": 4,
。。。               下面省略的都是没关系的

看这个其实就知道,color = red 的有四个,换句话说,去重功能(distinct)已经做出来了。

如果你要做平均值什么的,只需在里面这样添加

GET /cars/transactions/_search
{ 
   
   "size" : 0,
   "aggs": { 
   
      "colors": { 
   
         "terms": { 
   
            "field": "color"
         },
         "aggs": { 
    
            "avg_price": { 
    
               "avg": { 
   
                  "field": "price" 
               }
            }
         }
      }
   }
}

注意哈,这个第二个aggs是和上面的terms同一级。field这个单词可以理解为字段,即你选择的那个key

此外聚合允许多重聚合,相当于对一个属性进行group by后对另一个属性再进行group by
就是在里面再放一个aggs,全称是aggregations。其实你上面就是,只不过这第二个aggs没有创建桶。

GET /cars/transactions/_search
{ 
   
   "size" : 0,
   "aggs": { 
   
      "colors": { 
   
         "terms": { 
   
            "field": "color"
         },
         "aggs": { 
   
            "avg_price": { 
    "avg": { 
    "field": "price" }
            },
            "make" : { 
   
                "terms" : { 
   
                    "field" : "make"
                },
                "aggs" : { 
    
                    "min_price" : { 
    "min": { 
    "field": "price"} }, 
                    "max_price" : { 
    "max": { 
    "field": "price"} } 
                }
            }
         }
      }
   }
}

游标查询

游标查询很简单,首先

GET/test/_search?scroll=1m 
{

    "query":{
        "match_all":{}
    },
    "size" : 1
}

这一部分就是查询old_index下的所有数据,并且缓存下来,但是一次只向前台输出1条。
输出结果如下

{
    "_scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFm9aeEY4Q2VPU3IyNXloU2ptb1hhNHcAAAAAAAAAZRZsaHN6YVNqRVIwaWpMWXZvbnZacnF3",
    "took": 47,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": [
            {
                "_index": "test",
                "_type": "_doc",
                "_id": "nB3cK3wBYbxq_xzH5a0Z",
                "_score": 1.0,
                "_source": {
                    "title": "P908a"
                }
            }
        ]
    }
}

这里面需要关注的只有那个scroll_id
然后如果需要输出这第一条之后的结果的话,我们就要利用这个scroll_id
如下

localhost:9200/_search/scroll
{
"scroll" : "1m",
"scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFm9aeEY4Q2VPU3IyNXloU2ptb1hhNHcAAAAAAAAAZxZsaHN6YVNqRVIwaWpMWXZvbnZacnF3"
}

可以看到返回结果
如下

{
    "_scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFm9aeEY4Q2VPU3IyNXloU2ptb1hhNHcAAAAAAAAAahZsaHN6YVNqRVIwaWpMWXZvbnZacnF3",
    "took": 37,
    "timed_out": false,
    "terminated_early": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": [
            {
                "_index": "test",
                "_type": "_doc",
                "_id": "nR3dK3wBYbxq_xzHcq3X",
                "_score": 1.0,
                "_source": {
                    "title": "P909a",
                    "stat": "P909a"
                }
            }
        ]
    }
}

返回了第二条数据。
这其中的1m指的是id有效时长为1min,超过了会报如下的bug,因此记得像上面那样持续更新scroll_id的有效时长

"error": {
        "root_cause": [
            {
                "type": "search_context_missing_exception",
                "reason": "No search context found for id [106]"
            }
        ],
        "type": "search_phase_execution_exception",
        "reason": "all shards failed",
        "phase": "query",
        "grouped": true,
        "failed_shards": [
            {
                "shard": -1,
                "index": null,
                "reason": {
                    "type": "search_context_missing_exception",
                    "reason": "No search context found for id [106]"
                }
            }
        ],
        "caused_by": {
            "type": "search_context_missing_exception",
            "reason": "No search context found for id [106]"
        }
    },
    "status": 404
}

为什么会存在游标查询呢?这是为了解决如果你的确需要查询超过10000条数据(默认最大10000),那么这时候你显然是做不到一次返回给前台10000条以上,假入使用from,size,那么当你需要用到10000,到第20000条时,你就会在后台再查询一次,很显然会造成的结果就是,前台分页等待时间太长了,用户体验很不好,而游标查询在设定时间内查询到所有的数据并缓存下来,那么当你需要10000~20000条时,不用再查一次,只要读取就ok了。换句话说你只要给后台传送你的scroll_id后台就能准确知道你要哪个地方的数据。(特别适合用在前台滚轮向下查的时候)

分析器和动态映射

这一部分我不会讲你如何设置分析器,而是讲一讲默认的分析器,以及动态映射的一些容易被坑的点。

这个默认分析器主要是在你创建索引和搜索时会被es自动使用,用来对数据内容做分析。
而这个动态映射会在你往索引添加不存在的字段时会采用,用来对数据类型做分析。

分析器默认的是standard分析器,他会对你的text类型的数据进行分析以后再建索引,standard会把这个text字符串中的字母全部切换为小写,并且把空格去掉,还有一些没意义的词,比如(a,an)。记住只作用在text类上。这个小写是个隐藏坑。

那么哪些字段会被当做text呢?除了建索引的时候你自己设置的mapping中将某些字段上设置为的text类数据,还有就是动态映射(dynamic mapping的时候),就是当你往索引插入没有的字段时候调用的东西,他会自动识别,并给这个字段一个类型。而对于一些特定格式的数据会被动态映射定义为text类数据
他的默认规则如下:

来源为es官方文档,dynamic mapping这一节内容
来源为es官方文档,dynamic mapping这一段
我们默认dynamic是true。这后面三个,就是”2021-09-29″这个字符串可能会被当成date类,“1234”会被当成long类,而“aaa111″会被当成text类,而且带有一个后缀**.keyword**的keyword类。(建议看官方,不过官方文档读起来好累啊。)

再强调一哈,对于这里的text类数据,动态映射会将此数据同时生成一个,字段名.keyword的字段,数据相同但保存为keyword类,而不再是text类了。这里为什么要做这一步呢?这是因为text类数据他会调用分析器!!然后把数据分词,大写字母变小写等等,这造成的直接结果是,你查原来的数据你是查不到的,因为你就没有对这个原来的数据建索引。而keyword类不会调用分析器,因此动态映射帮你保留了一个字段名.keyword的字段,来让你可以精准查询。

{
    "took": 115,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": [
            {
                "_index": "test",
                "_type": "_doc",
                "_id": "nB3cK3wBYbxq_xzH5a0Z",
                "_score": 1.0,
                "_source": {
                    "title": "P908a"
                }
            },
            {
                "_index": "test",
                "_type": "_doc",
                "_id": "nR3dK3wBYbxq_xzHcq3X",
                "_score": 1.0,
                "_source": {
                    "title": "P909a",
                    "stat": "P909a"
                }
            }
        ]
    }
}

这里你只要关注source里面,总共有title和stat两个字段。其中title是我事先定义的,而stat是后来添加的,因此,title是会被standard分析器分析后建索引,stat则是在动态映射后,然后分析,再建索引。
由于分析器会将数据变为小写,因此如果这样写

{
    "query" : {
        "bool" : {
            "must":{
                "term" : {"title" : "P909a"}
            }
        }
    }
}

你是查不到的。
结果如下

{
    "took": 8,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 0,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    }
}

但你如果把里面的内容改成这样

"term" : {"title" : "p909a"}  //P切换为小写p

你就会看到

"hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 0.6931471,
        "hits": [
            {
                "_index": "test",
                "_type": "_doc",
                "_id": "nR3dK3wBYbxq_xzHcq3X",
                "_score": 0.6931471,
                "_source": {
                    "title": "P909a",
                    "stat": "P909a"
                }
            }
        ]
    }
}

我查到了数据。
title是我提前在mapping中定义为了text类,因此不会存在keyword字段。换句话说

"term" : {"title.keyword" : "P909a"}

是查不到的

"hits": {
        "total": {
            "value": 0,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    }

我们再来看stat这一字段
同理

{
    "query" : {
        "bool" : {
            "must":{
                "term" : {"stat" : "p909a"} //注意看p是小写的
            }
        }
    }
}

这样写可以查到数据

        "hits": [
            {
                "_index": "test",
                "_type": "_doc",
                "_id": "nR3dK3wBYbxq_xzHcq3X",
                "_score": 0.2876821,
                "_source": {
                    "title": "P909a",
                    "stat": "P909a"
                }
            }
            ]

此外,由于title用了动态映射,因此你可以这样精准查询

"term" : {"stat.keyword" : "P909a"}
"hits": [
            {
                "_index": "test",
                "_type": "_doc",
                "_id": "nR3dK3wBYbxq_xzHcq3X",
                "_score": 0.2876821,
                "_source": {
                    "title": "P909a",
                    "stat": "P909a"
                }
            }
        ]

综上所述,如果你事先定义了text类,而又使用了默认分析器,那么你要记住,你很有可能因为字母大小写,分词等原因无法精准查询(因为你的规则里没有额外给他定义为keyword类),而对于未定义的 那些被动态映射为text的字段,你可以用.keyword字段(这个是keyword类)来精准查询,也可以直接用原字段,根据分析器的规则来查询。

至于如何定制分析器,这里就不叙述了,我目前用不上。还有关于如何定制化映射,比如说在开头或者结尾看到什么字符就将字符串定义为date类什么的,我也一般情况下用不上,因此不叙述了。

建索引,mapping写法如下(mappings)

PUT "localhost:9200/bilibili"

{
   "mappings":{
       "properties":{
           "title" : {
               "type" :"text",
               "fields":{
              	 "keyword":{
               		"type":"keyword"
           }
       }      
   } 
}	

这样我就建了个“bilibili”的索引,里面事先定义了一个叫title的text类字段。
如果你看到,mapping里有个_doc记得删去,这个已经被废弃了,不需要加。

High Level Rest Api中一些常用API

我们后台写接口不可能像上面命令行那样操作数据库,因此es给了javaAPI让你能在后台操作es数据库。
一般而言写接口只涉及查询,举个例子,查询title字段为“p909a”

traceExceptionSearchRequest.source(new SearchSourceBuilder()
                    .timeout(TimeValue.timeValueSeconds(10))
                    .query(new BoolQueryBuilder()
                            .must(QueryBuilders.termQuery("title", "p909a"))
                            .must(QueryBuilders.rangeQuery(timeField)
                                    .gte(startTime)
                                    .lte(endTime)))
                    .size(0)
);

如果熟悉命令行操作的话,看这个应该很快就能理解。其他的都同理。
然后是调用,得到返回值

traceExceptionResponse = restHighLevelClient.search(traceExceptionSearchRequest, RequestOptions.DEFAULT);

至于如何取到返回值里你想要的数据
举个例子,想要取到_source里的字段

 List<Map> orderList = new ArrayList<>();
        for (SearchHit hit : busiOrderStatResponse.getHits().getHits()) {
            Map order = hit.getSourceAsMap();
            orderList.add(order);
        }

熟悉命令行应该也能很快看明白。(json数据的返回值中的一种结构,map数据结构,还可以是List,或者Object)
像聚合的操作

.aggregation(AggregationBuilders.terms(Name)
                            .size(Integer.MAX_VALUE)
                            .field(Field)));

取返回值

				List<String> virtualOrderIds = new ArrayList<>();
				
                Terms virtualOrderAggs = traceExceptionResponse.getAggregations().get(Name);
                for (MultiBucketsAggregation.Bucket virtualOrder : virtualOrderAggs.getBuckets()) {
                    virtualOrderIds.add((String) virtualOrder.getKey());
                }
            }

不解释,自行理解。
还有游标查询

searchRequest.scroll(TimeValue.timeValueMinutes(1L));
            searchRequest.source(searchSourceBuilder);   //searchSourceBuilder实现省略,里面放的是查询条件
 response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//第一次scroll查询
 
 String id = response.getScrollId();
 
SearchScrollRequest scrollRequest = new SearchScrollRequest(id);//后续的scroll查询
            scrollRequest.scroll(TimeValue.timeValueMinutes(1L));  //1L 应该就是1m,不确定,自行查找确认

这里提一提,因为索引id一般会在第二次调用接口的时候使用,而不是查了第一次后立马在这次调用中再查询第二次,因此,你要记得保存下来,然第二次调用接口的时候可以取到。

补充1:

.fetchSource(new String[]{ 
   "LogTime", "Info"}, null)
                            .sort("@timestamp", SortOrder.ASC)

fetchSource 对标 dsl中的_source,用来选择以及排除查询到的结果将返回给前台的字段。

dsl中写法如下

{ 
   
    "_source":{ 
   
        "includes":["title","url","id"],
        "excludes":["desc"]
    }
}

includes指代选择的,excludes指代排除的。两者关系为与

补充2:
dsl中的排序写法如下

"sort": { 
    "date": { 
    "order": "desc" }}

注意:日期格式的排序只支持UTC 如下例 “2999-11-16T02:59:52.000000Z”,如果格式不正确的话,将不会有任何返回值

对应的api为

.sort("date", SortOrder.DESC)

补充3:
java的api访问es步骤

1 引入es的api包

        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.4.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.elasticsearch</groupId>
                    <artifactId>elasticsearch</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.4.0</version>
        </dependency>

2 生成一个restHighLevelClient的bean来访问es
写法如下

@Configuration
public class ElasticsearchConfig { 

private static final String SEPARATOR_A = ",";
private static final String SEPARATOR_B = ":";
@Value("${spring.elasticsearch.rest.uris}")
public String esUrl; 例如127.92.37.111:56821,180.48.28.190:52990
/** * format: http://10.45.61.51:52900 * <p> * 超时时长改成2min * * @return RestHighLevelClient */
@Value("${spring.elasticsearch.jest.username}")
public String esUserName = "*****";
@Value("${spring.elasticsearch.jest.password}")
public String esPassWord = "******";
@Bean
public RestHighLevelClient highLevelClient() { 

List<HttpHost> httpHosts = new ArrayList<>();
String[] clients = StringUtils.split(esUrl, SEPARATOR_A);
for (String client:clients) { 

String[] urlFragments = StringUtils.split(client, SEPARATOR_B);
int port = Integer.parseInt(urlFragments[1]);
String host = urlFragments[0];
httpHosts.add(new HttpHost(host, port, "http"));
}
//上面这些部分是用来对esUrl的主机ip和端口号进行拆分的,并生成一个list<HttpHost>类
HttpHost[] httpHostArr = httpHosts.toArray(new HttpHost[0]);
RestClientBuilder restClientBuilder = RestClient.builder(httpHostArr);此处输入ip地址和端口号
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(esUserName, esPassWord));//此处输入账号和密码
restClientBuilder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { 

@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { 

httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
return httpClientBuilder;
}
});
return new RestHighLevelClient(restClientBuilder);
}
}

3 配置文件
一般bean,都使用配置文件修改配置,比如这里的ip,host,username,password,创建一个名为application.yml

spring:
elasticsearch:
rest:
uris: 127.92.37.111:56821,180.48.28.190:52990
jest:
username: *****
password: *****

这样就生成了可访问es的类了

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145932.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • CTK Plugin Framework 基本原理

    CTK Plugin Framework 基本原理“CTKPluginFramework技术是面向C++的动态模型系统。该系统允许插件之间的松散耦合,并且提供了设计良好的方式来进行功能和数据…

  • lamda表达式和三个例子

    lamda表达式和三个例子(参数)->{方法语句}这样的形式就是lamda表达式,不用定义参数和返回值的数据类型-可以省略的情况:只有一个参数的时候参数可以不用括号;只有一个语句的时候大括号可以不用;只有一个语句且是return的时候可以省略return,直接写需要返回的值(表达式)目录1、for循环实例2、多线程实例3、sort排序实例1、for循环实例这个实例展示了传入一个参数且无返回值的用法定义一个字符串数组并实例化,对这个数组进行操作。通常的打印所有元..

  • wireshark找不到接口win10_安装打印机找不到usb接口

    wireshark找不到接口win10_安装打印机找不到usb接口Win10下使用WireShark出现没有找到接口问题,无法抓取数据包解决:安装Win10Pcap。到http://www.win10pcap.org/download/下载该软件安装完成后,重启WireShark

    2022年10月24日
  • pycharm基本操作_pycharm用法

    pycharm基本操作_pycharm用法0前言1官方快捷键2自定义快捷键0前言1官方快捷键Ctrl+快捷键说明Ctrl+鼠标左键/B点击变量应用处,到达变量定义处点击变量定义处,显示变量应用列表Ctrl+C/X/V复制/剪切/粘贴Ctrl+D现场复制粘贴选中的区域或当前行Ctrl+F/R开启查找/替换功能,开启时自动填写选中的字符Ctrl+H显示当前class的层次结构Ctrl+S全部保存Ctrl+W扩大光标选中范围Ctrl.

  • Wget 下载系列小记|使用linux centos wget下载 oracle 数据库| How do I download oracle databases on centos using wge…

    Wget 下载系列小记|使用linux centos wget下载 oracle 数据库| How do I download oracle databases on centos using wge…一、wget下载说明1、普通版wget”download_url”-Ofile_nameORcurl”download_url”-ofile_name2wget–load-cookies=./cookies.txt–no-check-certificate”file_url”-Ofile_nameORcurl–location–cooki…

  • tinyXml解析XML文件

    tinyXml解析XML文件TinyXML解析一个XML文档,并从该文档构建可读取、修改和保存的文档对象模型(DOM)。XML代表“可扩展标记语言”,它允许您创建您自己的文档标记。HTML在标记方面做得很好用于浏览器的文档,XML允许您定义任何类型的文档标记,例如描述组织者应用程序。XML是一种非常结构化和方便的格式。所有为存储应用程序数据而创建的随机文件格式都可以全部替换为XML。所有内…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号