查询(CQL、ECQL、FilterFactory)

        不管是通过哪种方法实现shp数据的查询,我们发现都离不开Filter,对于GeoTools,提供了两种创建Filter的方法,他们分别是CQLFilterFactory

一、CQL

   参考:CQL — GeoTools 32-SNAPSHOT User Guide

        CQL 实用工具类由静态方法组成,您可以调用这些方法来将文本 String 转换为 Expression、Filter 或 List<Filter>。它还能够获取这些项目并生成适当的文本表示。

1、单个filter

Filter filter = CQL.toFilter("attName >= 5");

2、解析filter列表

        使用“;”字符分隔出每个filter:

List filters = CQL.toFilterList("att1 > 5;ogc:name = 'river'");

3、通过比较值进行筛选

Filter result = CQL.toFilter("ATTR1 < (1 + ((2 / 3) * 4))" );
Filter result = CQL.toFilter("ATTR1 < abs(ATTR2)" );
Filter result = CQL.toFilter("ATTR1 < 10 AND ATTR2 < 2 OR ATTR3 > 10" );

4、使用文本进行筛选

Filter result = CQL.toFilter( "ATTR1 LIKE 'abc%'" );
Filter result = CQL.toFilter( "ATTR1 NOT LIKE 'abc%'" );

5、过滤空值

Filter result = CQL.toFilter( "ATTR1 IS NULL" );
Filter result = CQL.toFilter( "ATTR1 IS NOT NULL" );
Filter by Comparing Time values

6、通过比较时间值进行筛选

//等于
Filter result = CQL.toFilter( "ATTR1 TEQUALS 2006-11-30T01:30:00Z" );
//日期之前
Before filter = (Before) CQL.toFilter("lastEarthQuake BEFORE 2006-11-30T01:30:00Z");
//周期之前
Filter result = CQL.toFilter( "ATTR1 BEFORE 2006-11-30T01:30:00Z/2006-12-31T01:30:00Z" );
//日期之后
After filter = (After) CQL.toFilter("lastEarthQuake AFTER 2006-11-30T01:30:00Z");
//使用GMT+3区的之后一个时间
After filter = (After) CQL.toFilter("lastEarthQuake AFTER 2006-11-30T01:30:00+03:00");
//周期之后
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/2006-12-31T01:30:00Z" );
//具有持续时间的时间谓词(2006-11-30T01:30:00Z 之后的十天)
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/P10D" );
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/T10H" );

//During predicate
  During filter =
                (During)
                        CQL.toFilter(
                                "lastEarthQuake DURING 1700-01-01T00:00:00/2011-01-01T00:00:00");

7、基于存在性进行筛选

Filter result = CQL.toFilter( "ATTR1 EXISTS" );
ilter result = CQL.toFilter( "ATTR1 DOES-NOT-EXIST" );

8、通过检查值是否介于之间来过滤

Filter result = CQL.toFilter( "ATTR1 BETWEEN 10 AND 20" );

9、使用复合属性

Filter result = CQL.toFilter( "gmd:MD_Metadata.gmd:identificationInfo.gmd:MD_DataIdentification.gmd:abstract LIKE  'abc%'" );

10、使用几何关系进行筛选

Filter result = CQL.toFilter( "CONTAINS(ATTR1, POINT(1 2))" );
Filter result = CQL.toFilter( "BBOX(ATTR1, 10,20,30,40)" );
Filter result = CQL.toFilter( "DWITHIN(ATTR1, POINT(1 2), 10, kilometers)" );
Filter result = CQL.toFilter( "CROSS(ATTR1, LINESTRING(1 2, 10 15))" );
Filter result = CQL.toFilter( "INTERSECT(ATTR1, GEOMETRYCOLLECTION (POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20)) )" );
Filter result = CQL.toFilter( "CROSSES(ATTR1, LINESTRING(1 2, 10 15))" );
Filter result = CQL.toFilter( "INTERSECTS(ATTR1, GEOMETRYCOLLECTION (POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20)) )" );

11、九交模式判断过滤器

Filter filter =ECQL.toFilter("RELATE(geometry, LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");

二、ECQL

        参考:ECQL — GeoTools 32-SNAPSHOT User Guide

        ECQL 语言旨在作为 CQL 的扩展,因此您可以编写 CQL 支持的所有谓词,并使用新语法规则中定义的新表达式可能性。

ECQL 实用程序类与方法兼容,允许您将其用作 CQL 的直接替代品。

1、 按比较值过滤

 Filter filter = ECQL.toFilter("1000 <= population"); 
 Filter filter =ECQL.toFilter("(under18YearsOld * 19541453 / 100 ) < (over65YearsOld * 19541453 / 100 )"); 
 Filter filter = ECQL.toFilter("population BETWEEN 10000000 and 20000000");
 Filter filter =ECQL.toFilter("area(Polygon((10 10, 20 10, 20 20, 10 10))) BETWEEN 10000 AND 30000");

2、按Features’ ID列表筛选

        筛选器 XML 格式允许定义捕获一组 FeatureID(通常表示所选内容)的 Id 筛选器。

//使用字符串作为 id:
Filter filter = ECQL.toFilter("IN ('river.1', 'river.2')");
//使用整数作为 id:
Filter filter = ECQL.toFilter("IN (300, 301)");
//我们尝试了几个实验,但并非所有实验都有效,留下了以下已弃用的语法:
Filter filter = ECQL.toFilter("ID IN ('river.1', 'river.2')");

3、基于一组值的筛选器

        以下过滤器选择以银、石油或黄金为主要矿产资源的国家:

Filter filter = ECQL.toFilter("principalMineralResource IN ('silver','oil', 'gold' )");

4、使用文本模式进行筛选

//使用LIKE关键字过滤文本模式:
Filter filter = ECQL.toFilter("cityName LIKE 'New%'");
//带有ILIKE关键字的不区分大小写示例
Filter filter = ECQL.toFilter("cityName ILIKE 'new%'");
//ECQL 允许您测试任何两个表达式,包括文字: 
Filter filter = ECQL.toFilter("'aabbcc' LIKE '%bb%'");

5、按空间关系过滤

Filter filter = ECQL.toFilter("DISJOINT(the_geom, POINT(1 2))");
Filter filter = ECQL.toFilter("DISJOINT(buffer(the_geom, 10) , POINT(1 2))");
Filter filter = ECQL.toFilter("DWITHIN(buffer(the_geom,5), POINT(1 2), 10, kilometers)");

6、按时间关系过滤

        时间谓词允许在两个给定的时间瞬间之间建立关系,或者在瞬间和时间间隔之间建立关系。在下一个示例中,during 谓词用于筛选在指定日期之间发生地震的城市

During filter =(During)ECQL.toFilter("lastEarthQuake DURING 1700-01-01T00:00:00Z/2011-01-01T00:00:00Z");

        在 ECQL 中,您可以在时间谓词的左侧编写日期时间表达式:

Filter filter = ECQL.toFilter("2006-11-30T01:00:00Z AFTER 2006-11-30T01:30:00Z");

在 Before 谓词中:

 Filter filter = ECQL.toFilter("2006-11-30T01:00:00Z BEFORE 2006-11-30T01:30:00Z");

在During谓词中:

 Filter filter =ECQL.toFilter("2006-11-30T01:00:00Z DURING 2006-11-30T00:30:00Z/2006-11-30T01:30:00Z ");

以下示例显示了一个时间谓词,该谓词在日期时间表达式中包含 UTC 时区 (GMT +3):

 Filter filter =ECQL.toFilter("2006-11-30T01:00:00+03:00 DURING 2006-11-30T00:30:00+03:00/2006-11-30T01:30:00+03:00 ");

 7、过滤空值

Filter filter = ECQL.toFilter(" Name IS NULL");
Filter filter = ECQL.toFilter("centroid( the_geom ) IS NULL");

8、属性存在谓词

Filter resultFilter = ECQL.toFilter("aProperty EXISTS");

9、表达式

Expression expr = ECQL.toExpression("X + 1");

10、filter 列表

//过滤器列表仍然支持使用“;” 分隔条目:
List<Filter> list = ECQL.toFilterList("X=1; Y<4");

11、使用日期文字过滤

Filter filter = ECQL.toFilter("foo = 1981-06-20");
Filter filter = ECQL.toFilter("foo <= 1981-06-20T12:30:01Z");

三、 FilterFactory

        参考:Filter — GeoTools 32-SNAPSHOT User Guide

        通过使用 FilterFactory ,您可以手动创建对象。 FilterFactory 接口仅限于严格遵守规范。

1、FilterFactory基础

        核心过滤器抽象在这里。这组接口是封闭的(你不能创建一个新的筛选器类并期望它工作)。

 

2、比较

        筛选器数据模型的核心是属性比较;通过这些过滤器,您可以测试要素的属性,并仅选择与以下要素匹配的要素:

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();Filter filter;
// 与equals相同功能的查询
ff.equal(ff.property("land_use"), ff.literal("URBAN"));
// 判断值是否为null
filter = ff.isNull(ff.property("approved"));
filter = ff.less(ff.property("depth"), ff.literal(300));
filter = ff.lessOrEqual(ff.property("risk"), ff.literal(3.7));
filter = ff.greater(ff.property("name"), ff.literal("Smith"));
filter = ff.greaterOrEqual(ff.property("schedule"), ff.literal(new Date()));
// 判断两个数值之前的结果
filter = ff.between(ff.property("age"), ff.literal(20), ff.literal("29"));
filter = ff.between(ff.property("group"), ff.literal("A"), ff.literal("D"));
// 不等于的简写
filter = ff.notEqual(ff.property("type"), ff.literal("draft"));
// like模式的过滤器
filter = ff.like(ff.property("code"), "2300%");
// 您可以使用自定义的通配符
filter = ff.like(ff.property("code"), "2300?", "*", "?", "\\");

3、Null vs Nil

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Filter filter;
// 如果approved等于“null”,正如前面的测试示例
filter = ff.isNull(ff.property("approved"));
// 此示例检查是否存在approved
filter = ff.isNil(ff.property("approved"), "no approval available");

4、MatchAction(匹配操作)

        实现该MultiValuedFilter接口的所有过滤器都支持对在评估时返回多个值的操作数进行过滤。可以通过MatchAction属性修改这些过滤器处理多个值的方式。可以通过一个简单的 getter 来检索该属性:

filter.getMatchAction()

MatchAction 有三个可能的值:

        MatchAction.ANY—当没有MatchAction指定时,它被设置为默认值MatchAction.ANY。如果任何可能的操作数组合的计算结果为真,则计算结果为真:

List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});
Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ANY);
System.out.println("Any: " + filter.evaluate(null)); // prints Any: true

        MatchAction.ALL — 如果所有可能的操作数组合的计算结果为真,则计算结果为真。:

List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});
Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ALL);
System.out.println("All: " + filter.evaluate(null)); 
// prints All: false

MatchAction.ONE — 如果恰好一种可能的值组合评估为真,则评估为真:

List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});
Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ONE);
System.out.println("One: " + filter.evaluate(null)); 
// prints One: true

5、逻辑过滤器

可以使用通常的 AND、OR 和 NOT 二进制逻辑组合过滤器。

filter = ff.not(ff.like(ff.property("code"), "230%"));
//您还可以组合筛选器以缩小返回的结果范围
filter = ff.and(ff.greater(ff.property("rainfall"), ff.literal(70)),ff.equal(ff.property("land_use"), ff.literal("urban"), false));
filter = ff.or(ff.equal(ff.property("code"), ff.literal("approved")),ff.greater(ff.property("funding"), ff.literal(23000)));

 6、Identifier(标识符

        另一种有趣的方法是使用过滤器来更多地作为 GIS 意义上的“选择”。在这种情况下,我们将直接匹配 FeatureId 而不是计算属性。

最常见的测试是针对 FeatureId :

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Filter filter = ff.id(ff.featureId("CITY.98734597823459687235"), ff.featureId("CITY.98734592345235823474"));

从形式上讲,这种 Id 匹配风格不应与传统的基于属性的评估(例如边界框过滤器)混合使用。您还可以使用Set<FeatureId>

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Set<FeatureId> selected = new HashSet<>();
selected.add(ff.featureId("CITY.98734597823459687235"));
selected.add(ff.featureId("CITY.98734592345235823474"));
Filter filter = ff.id(selected);

另一个使用标识符的地方是在处理版本信息时。在这种情况下ResourceId,使用由 afid和 a组成的a ridResourceId 可用于探索版本信息:

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Filter filter;
// 获取特定修订版本
filter = ff.id(ff.featureId("CITY.98734597823459687235", "A457"));
// 您还可以使用ResourceId获取特定的修订版本
filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version()));
//获取符合条件的上一个
filter =ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(Action.PREVIOUS)));
// 获取符合条件的下一个
filter =ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(Action.NEXT)));
// 获取第一个
filter =ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(Action.FIRST)));
// 获取第一个(即索引=1)
filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(1)));
// 获取序列中的第12条记录(即索引=12)
filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(12)));
// 获取接近1985年1月数据
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);df.setTimeZone(TimeZone.getTimeZone("GMT"));
filter =ff.id(ff.resourceId("CITY.98734597823459687235","A457",new Version(df.parse("1985-1-1"))));
// 获取90年代所有的实体对象
filter =ff.id(ff.resourceId("CITY.98734597823459687235",df.parse("1990-1-1"),df.parse("2000-1-1")));

7、Spatial(空间过滤器)

这是一个快速示例,展示了如何在边界框中请求特征。

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
ReferencedEnvelope bbox = new ReferencedEnvelope(x1, x2, y1, y2, DefaultGeographicCRS.WGS84);
Filter filter = ff.bbox(ff.property("the_geom"), bbox);

 8、Temporal(时态过滤器)

gt-main 模块提供了一些我们需要的实现类:

  • DefaultIntant:这是用于表示单个时间点的 Instant 的实现。
  • DefaultPeriod:这是Period的一个实现,用于表示时间范围。

这是一个示例,说明它们的构造和与时间过滤器的使用:

// 使用gt main的默认实现
DateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
Date date1 = FORMAT.parse("2001-07-05T12:08:56.235-0700");
Instant temporalInstant = new DefaultInstant(new DefaultPosition(date1));
// 简单检查属性是否在
Filter after = ff.after(ff.property("date"), ff.literal(temporalInstant));
// 也可以在一定期限内检查属性
Date date2 = FORMAT.parse("2001-07-04T12:08:56.235-0700");
Instant temporalInstant2 = new DefaultInstant(new DefaultPosition(date2));
Period period = new DefaultPeriod(temporalInstant, temporalInstant2);
Filter within = ff.toverlaps(ff.property("constructed_date"), ff.literal(period));

8、Expression(表达式)

        上面提到的许多过滤器都是作为两个(或多个)表达式之间的比较呈现的。表达式用于访问保存在 Feature(或 POJO、Record 或 …)中的数据。核心表达式抽象在这里 - 该集合是开放的,您可以定义新函数。

表达式非常有用,您会在 GeoTools 的许多部分中看到它们弹出。样式使用它们来选择用于描绘的数据等等。

  • PropertyName

    PropertyName表达式用于从您的数据模型中提取信息。最常见的用途是访问特征属性。

    FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2( GeoTools.getDefaultHints() );
    Expression expr = ff.property("name");
    //计算
    Object value = expr.evaluate( feature );
    if( value instanceof String){ 
       name = (String)value;
    }else{
       name = "(invalid name)";
    }
    

    您还可以将值具体要求为字符串,如果无法将该值强制为字符串,则将返回 null:

    FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2( GeoTools.getDefaultHints() );
    Expression expr = ff.property("name");
    String name = expr.evaluate( feature, String );
     //evaluate
    if( name == null ){
        name = "(invalid name)";
    }
    
  • X 路径和命名空间

    可以在过滤器中使用 XPath 表达式。这对于针对复杂特征评估嵌套属性特别有用。要评估 XPath 表达式,org.xml.sax.helpers.NamespaceSupport需要一个 对象来将前缀与名称空间 URI 相关联。FilterFactory2支持创建PropertyName具有关联命名空间上下文信息的表达式:

    FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2( GeoTools.getDefaultHints() );
    NamespaceSupport namespaceSupport = new NamespaceSupport();
    namespaceSupport.declarePrefix("foo", "urn:cgi:xmlns:CGI:GeoSciML:2.0" );
    Filter filter = ff.greater(ff.property("foo:city/foo:size",namespaceSupport),ff.literal(300000));
    

    可以从现有PropertyName表达式中检索命名空间上下文信息:

    PropertyName propertyName = ff.property("foo:city/foo:size", namespaceSupport);
    NamespaceSupport namespaceSupport2 = propertyName.getNamespaceContext();
    

  • Functions(功能)

    您可以使用FilterFactory2以下方法创建函数:

    FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2( GeoTools.getDefaultHints() );
    PropertyName a = ff.property("testInteger");
    Literal b = ff.literal( 1004.0 );
    Function min = ff.function("min", a, b );
    

    对于需要多个参数的函数,您需要使用数组:

    FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
    PropertyName property = ff.property("name");
    Literal search = ff.literal("foo");
    Literal replace = ff.literal("bar");
    Literal all = ff.literal( true );
    Function f = ff.function("strReplace", new Expression[]{property,search,replace,all});
    

    找不到函数时该怎么办 - 创建函数将失败!Symbology Encoding 2.0 规范的概念是fallbackValue- 虽然我们还没有通过工厂提供它,但您可以使用FunctionFinder.

    FunctionFinder finder = new FunctionFinder(null);
    finder.findFunction("pi", Collections.emptyList(), ff.literal(Math.PI));
    

9、FilterVisitor(过滤器访问者

 FilterVisitor用于遍历过滤器数据结构。常见用途包括:

  •      询问有关过滤器内容的问题
  •      对过滤器执行分析和优化(比如用“2”替换“1+1”)
  •      转换过滤器(想想搜索和替换)

 当使用 XSLT 处理遍历树时,对 XML 文档(也形成树)使用类似的方法。

所有这些活动都有一个共同点:

        需要检查过滤器的内容
        需要建立结果或答案
 这是一个快速代码示例,显示了使用访问者遍历数据结构:

class FindNames extends DefaultFilterVisitor {
    public Set<String> found = new HashSet<String>();
    //我们只感兴趣属性名称表达式
    public Object visit( PropertyName expression, Object data ) {
        found.add( expression.getPropertyName() );
        return found;
    }
}
// 将访问者传递到过滤器以开始遍历
FindNames visitor = new FindNames();
filter.accept( visitor, null );
System.out.println("Property Names found "+visitor.found );

<think>好的,用户想在Ubuntu系统上配置Hadoop分布式集群的单机模式。首先,我需要明确“单机模式”在Hadoop中的定义。Hadoop的单机模式其实就是本地模式,不需要启动任何守护进程,所有程序都在单个JVM上运行。这点和伪分布式模式不同,伪分布式虽然是在单机上运行,但模拟了集群环境,需要配置SSH无密码登录,并修改多个配置文件如core-site.xml、hdfs-site.xml等。而单机模式通常不需要修改这些配置,直接使用默认设置即可。 接下来,我需要回顾Hadoop安装步骤。根据引用[4]提到的实验准备,需要安装JDK和Hadoop。用户可能已经下载了这些包,但需要确认版本是否兼容。例如,Hadoop 2.10.1需要JDK 8的支持。安装过程包括解压、设置环境变量等。 然后,检查用户是否需要配置单机模式,还是可能混淆了单机模式和伪分布式。根据用户的问题,明确是单机模式,所以不需要修改配置文件,但可能需要确保Java环境正确。需要提醒用户验证Java安装,并检查Hadoop的环境变量配置是否正确。 另外,引用[1]提到伪分布式的配置和分布式类似,但单机模式更简单。可能需要指出两者的区别,避免用户误操作。例如,单机模式不需要配置SSH,也不需要修改xml文件中的地址和端口。 还要考虑用户可能遇到的常见问题,例如环境变量未正确设置,导致Hadoop无法找到Java路径。这时候需要指导用户编辑hadoop-env.sh文件,设置JAVA_HOME变量。 最后,生成验证步骤,比如运行一个简单的MapReduce作业,例如wordcount示例,来确认安装是否成功。同时,提醒用户查看日志,如果有错误发生,可以检查日志文件进行排查。 总结步骤:安装JDK -> 安装Hadoop -> 配置环境变量 -> 验证安装 -> 运行测试。确保每个步骤都清晰,用户能按部就班操作。同时,注意引用相关参考资料,如引用[4]提供的安装包链接,方便用户下载所需软件。</think>### 在Ubuntu中配置Hadoop单机模式(本地模式)的步骤 #### 1. 环境准备 - **操作系统**: Ubuntu 18.04/20.04(其他版本方法类似) - **Java环境**: 需安装JDK 8(Hadoop 2.x兼容JDK 8,若使用Hadoop 3.x需JDK 11+)[^4] - **Hadoop版本**: 推荐使用hadoop-2.10.1或更高版本 #### 2. 安装JDK ```bash # 解压JDK安装包(假设下载路径为~/Downloads) sudo tar -zxvf ~/Downloads/jdk-8u11-linux-x64.tar.gz -C /usr/local/ # 配置环境变量 echo 'export JAVA_HOME=/usr/local/jdk1.8.0_11' >> ~/.bashrc echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc source ~/.bashrc ``` #### 3. 安装Hadoop ```bash # 解压Hadoop安装包 sudo tar -zxvf ~/Downloads/hadoop-2.10.1.tar.gz -C /usr/local/ # 重命名文件夹 sudo mv /usr/local/hadoop-2.10.1 /usr/local/hadoop # 配置环境变量 echo 'export HADOOP_HOME=/usr/local/hadoop' >> ~/.bashrc echo 'export PATH=$HADOOP_HOME/bin:$PATH' >> ~/.bashrc source ~/.bashrc ``` #### 4. 验证单机模式 单机模式无需额外配置,直接运行测试命令: ```bash # 创建测试输入目录 mkdir ~/input echo "hello hadoop" > ~/input/test.txt # 运行Hadoop自带的wordcount示例 hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar wordcount ~/input ~/output # 查看结果 cat ~/output/* ``` #### 5. 关键配置文件说明(单机模式无需修改) - `$HADOOP_HOME/etc/hadoop/hadoop-env.sh`: 确认`JAVA_HOME`路径正确 ```bash export JAVA_HOME=/usr/local/jdk1.8.0_11 ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值