iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Scala文件操作方法有哪些
  • 315
分享到

Scala文件操作方法有哪些

2023-07-05 21:07:28 315人浏览 安东尼
摘要

本篇内容介绍了“Scala文件操作方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 读取数据在Scala语言的  So

本篇内容介绍了“Scala文件操作方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    1. 读取数据

    在Scala语言的  Source单例对象 中, 提供了一些非常便捷的方法, 从而使开发者可以快速的从指定数据源(文本文件, URL地址等)中获取数据, 在使用 Source单例对象 之前, 需要先导包, 即 import scala.io.Source.

    1.1 按行读取

    我们可以以 行 为单位, 来读取数据源中的数据, 返回值是一个 迭代器类型的对象 . 然后通过 toArray, toList 方法, 将这些数据放到数组或者列表中即可.

    注意: Source类扩展自Iterator[Char]

    格式

    //1. 获取数据源文件对象.val source:BufferedSource = Source.fromFile("数据源文件的路径","编码表")//2. 以行为单位读取数据.val lines:Iterator[String] = source.getLines()//3. 将读取到的数据封装到列表中.val list1:List[String] = lines.toList//4. 千万别忘记关闭Source对象.source.close()

    需求

    在当前项目下创建data文件夹, 并在其中创建1.txt文本文件, 文件内容如下:

    好好学习, 天天向上!
    hadoop, ZooKeeper, Flume, spark
    flink, Sqoop, HBase

    以行为单位读取该文本文件中的数据, 并打印结果.

    参考代码

    import scala.io.Sourceobject ClassDemo01 {def main(args: Array[String]): Unit = {//1. 获取数据源对象.val source = Source.fromFile("./data/1.txt")//2.通过getLines()方法, 逐行获取文件中的数据.var lines: Iterator[String] = source.getLines()//3. 将获取到的每一条数据都封装到列表中.val list1 = lines.toList//4. 打印结果for(s <- list1) println(s)//5. 记得关闭source对象.source.close()}}

    1.2 按字符读取

    Scala还提供了 以字符为单位读取数据 这种方式, 这种用法类似于迭代器, 读取数据之后, 我们可以通过 hasNext(),next()方法 , 灵活的获取数据.

    Scala使用source.buffered方法按字符读取文件

    什么是source.buffered方法

    source.buffered方法是scala.io.Source类的一个成员方法,它可以将一个Source对象转换为一个BufferedSource对象。BufferedSource对象是一个实现了BufferedIterator特质的对象,它可以提供一个缓冲区,用来存储未被消费的元素。这样可以提高读取文件的效率,也可以方便地查看下一个元素而不消费它。

    如何使用source.buffered方法

    要使用source.buffered方法,首先需要导入scala.io.Source类,并创建一个Source对象,然后调用其buffered方法,得到一个BufferedSource对象。例如,以下代码创建了一个从文件中获取的Source对象,并转换为一个BufferedSource对象:

    import scala.io.Source// 创建一个从文件中获取的Source对象val source = Source.fromFile("demo.txt")// 调用buffered方法,得到一个BufferedSource对象val buffered = source.buffered

    然后,可以使用BufferedSource对象的各种方法来按字符读取文件。例如,以下代码使用head方法来查看下一个字符,使用next方法来消费下一个字符,并打印出来:

    // 使用head方法查看下一个字符println(buffered.head)// 使用next方法消费下一个字符println(buffered.next())// 继续使用head方法查看下一个字符println(buffered.head)// 继续使用next方法消费下一个字符println(buffered.next())

    最后,需要关闭Source对象,释放资源:

    // 关闭Source对象source.close()
    一个示例

    下面给出一个完整的示例,演示如何使用source.buffered方法来按字符读取文件,并打印出来。

    首先,准备一个文本文件demo.txt,内容如下:

    Hello, Scala!
    你好,Scala!

    然后,编写以下代码:

    import scala.io.Source// 创建一个从文件中获取的Source对象,并转换为一个BufferedSource对象val buffered = Source.fromFile("demo.txt").buffered// 循环判断是否有下一个元素while (buffered.hasNext) {  // 打印出当前元素,并消费掉  print(buffered.next())}// 关闭Source对象buffered.close()

    最后,运行程序,可以看到输出结果如下:

    Hello, Scala!
    你好,Scala!

    1.3 读取词法单元和数字

    所谓的词法单元指的是 以特定符号间隔开的字符串 , 如果数据源文件中的数据都是 数字形式的字符串 , 我们可以很方便的从文件中直接获取这些数据, 例如:

    10 2 5

    11 2

    5 1 3 2

    格式

    //1. 获取数据源文件对象.val source:BufferedSource = Source.fromFile("数据源文件的路径","编码表")//2. 读取词法单元.// \s表示空白字符(空格, \t, \r, \n等)val arr:Array[String] = source.mkString.split("\\s+")//3. 将字符串转成对应的整数val num = strNumber.map(_.toInt)//4. 千万别忘记关闭Source对象.source.close()

    参考代码

    将上面的数字存入2.txt

    import scala.io.Sourceobject ClassDemo03 {def main(args: Array[String]): Unit = {val source = Source.fromFile("./data/2.txt")// \s表示空白字符(空格, \t, \r, \n等)val strNumber = source.mkString.split("\\s+")//将字符串转成对应的整数val num = strNumber.map(_.toInt)for(a <- num) println(a + 1)}}

    1.4 从URL或者其他源读取数据

    Scala中提供了一种方式, 可以让我们直接从指定的URL路径, 或者其他源(例如: 特定的字符串)中直接读取数据。

    格式 从URL地址中读取数据

    //1. 获取数据源文件对象.val source = Source.fromURL("https://www.csdn.net")//2. 将数据封装到字符串中并打印.println(source.mkString)

    从其他源读取数据

    //1. 获取数据源文件对象.val str = Source.fromString("CSDN")println(str.getLines())

    需求

    • 读取Https://www.csdn.net 页面的数据, 并打印结果.

    • 直接读取字符串 CSDN, 并打印结果.

    参考代码

    import scala.io.Sourceobject ClassDemo04 {def main(args: Array[String]): Unit = {val source = Source.fromURL("https://www.csdn.net")println(source.mkString)val str = Source.fromString("CSDN")println(str.getLines())}}

    1.5 读取二进制文件

    Scala没有提供读取二进制文件的方法, 我们需要通过Java类库来实现.

    需求 已知项目的data文件夹下有 05.png 这张图片, 请读取该图片数据, 并将读取到的字节数打印到控制台上. 参考代码

    object ClassDemo05 {def main(args: Array[String]): Unit = {val file = new File("./data/04.png")val fis = new FileInputStream(file)val bys = new Array[Byte](file.length().toInt)fis.read(bys)fis.close()println(bys.length)}}

    2. 写入文件

    2.1 使用java.io.PrintWriter类

    java.io.PrintWriter类是Java提供的一个用于写入文本文件的类。它可以接受一个文件、一个输出流或一个字符串作为参数,然后通过调用其write方法来向文件中写入数据。例如,以下代码可以向文件hello.txt中写入一行文本:

    import java.io._val pw = new PrintWriter(new File("hello.txt"))pw.write("Hello, world")pw.close()

    使用PrintWriter类写入文件时,有两个需要注意的问题:

    • PrintWriter类不会抛出异常,而是设置一个布尔标志来表示是否发生了错误。可以通过调用其checkError方法来检查是否有错误发生。

    • PrintWriter类默认使用平台的字符编码,这可能导致不同平台之间的不一致。可以通过指定字符编码来避免这个问题。例如,以下代码使用了UTF-8编码:

    val pw = new PrintWriter(new File("hello.txt"), "UTF-8")

    2.2 使用java.io.FileWriter类

    java.io.FileWriter类是Java提供的另一个用于写入文本文件的类。它是OutputStreamWriter的子类,可以接受一个文件或一个字符串作为参数,然后通过调用其write方法来向文件中写入数据。例如,以下代码可以向文件test.txt中写入一行文本:

    import java.io._val file = new File("test.txt")val bw = new BufferedWriter(new FileWriter(file))bw.write("We are learning Scala programming language")bw.close()

    使用FileWriter类写入文件时,有两个需要注意的问题:

    • FileWriter类会抛出IOExceptions异常,所以需要使用try-catch-finally语句来处理异常和关闭文件。

    • FileWriter类默认使用平台的字符编码,这可能导致不同平台之间的不一致。FileWriter类没有提供指定字符编码的构造器,所以如果需要指定字符编码,可以使用OutputStreamWriter类代替。例如,以下代码使用了UTF-8编码:

    val file = new File("test.txt")val bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))bw.write("We are learning Scala programming language")bw.close()

    2.3 使用java.io.FileOutputStream类

    FileOutputStream是Java提供的一个用于写入二进制文件的类,它是OutputStream的子类,可以接受一个文件或一个字符串作为参数,然后通过调用其write方法来向文件中写入字节数据。例如,以下代码可以向文件data.bin中写入一个整数:

    import java.io._val file = new File("data.bin")val fos = new FileOutputStream(file)fos.write(42)fos.close()

    FileOutputStream类会抛出IOExceptions异常,所以需要使用try-catch-finally语句来处理异常和关闭文件。

    已收到消息. FileOutputStream是Java提供的一个用于写入二进制文件的类,它是OutputStream的子类,可以接受一个文件或一个字符串作为参数,然后通过调用其write方法来向文件中写入字节数据。例如,以下代码可以向文件data.bin中写入一个整数: scala import java.io._ val file = new File("data.bin") val fos = new FileOutputStream(file) fos.write(42) fos.close() FileOutputStream类会抛出IOExceptions异常,所以需要使用try-catch-finally语句来处理异常和关闭文件。

    2.4 几种写入的区别

    因为FileOutputStream类有两种构造器,一种接受一个文件对象作为参数,一种接受一个字符串作为参数。如果接受一个字符串作为参数,那么它会自动创建一个文件对象。所以,以下两种写法是等价的:

    val pw = new FileOutputStream(new File("./data/3.txt"))val pw = new FileOutputStream("./data/3.txt")

    但是,如果使用PrintWriter类或FileWriter类,那么就必须使用一个文件对象作为参数,不能使用一个字符串。所以,以下两种写法是不等价的:

    val pw = new PrintWriter(new File("./data/3.txt"))val pw = new PrintWriter("./data/3.txt") // 错误

    2.5 使用第三方库

    除了Java提供的类之外,还有一些第三方库可以用来写入文件,例如Apache Commons IO1和os-lib2等。这些库通常提供了更简洁和高效的api,也支持更多的功能和格式。例如,以下代码使用了Apache Commons IO库的FileUtils类来写入文件,并指定了字符编码:

    import org.apache.commons.io.FileUtilsFileUtils.writeStringToFile(new File("test.txt"), "Hello, world", "UTF-8")

    以下代码使用了os-lib库的os.File对象来写入文件,并返回一个字节长度:

    import os._os.write(os.pwd / "test.txt", "Hello, world")

    3. Scala序列化和反序列化

    3.1 什么是序列化和反序列化

    在Scala中,如果想将对象传输到其他虚拟机,或者临时存储,就可以通过序列化和反序列化来实现了。

    • 序列化:把对象写到文件中的过程。

    • 反序列化:从文件中加载对象的过程。

    3.2 如何实现序列化和反序列化

    一个类的对象要想实现序列化和反序列化操作,则该类必须继承Serializable特质。Serializable特质是一个空特质,它没有任何方法和字段,只是用来标记一个类是否可以被序列化和反序列化。

    要实现序列化操作,可以使用java.io.ObjectOutputStream类,它是一个用于写入对象的输出流。它可以接受一个输出流作为参数,然后通过调用其writeObject方法来写入对象。

    要实现反序列化操作,可以使用java.io.ObjectInputStream类,它是一个用于读取对象的输入流。它可以接受一个输入流作为参数,然后通过调用其readObject方法来读取对象。

    3.3 一个示例

    下面给出一个简单的示例,演示如何使用case class和object来实现序列化和反序列化操作。

    首先,定义一个case class Person,属性为姓名和年龄。注意,case class会自动继承Serializable特质,所以不需要显式地写出来。

    // 定义一个case class Personcase class Person(var name: String, var age: Int)

    然后,创建Person样例类的对象p,并通过序列化操作将对象p写入到项目下的data文件夹下的4.txt文本文件中。

    // 创建Person样例类的对象pval p = Person("张三", 23)// 创建一个ObjectOutputStream对象,传入一个输出流作为参数val oos = new ObjectOutputStream(new FileOutputStream("./data/4.txt"))// 调用writeObject方法,将对象p写入到输出流中oos.writeObject(p)// 关闭输出流oos.close()

    最后,通过反序列化操作从项目下的data文件夹下的4.txt文件中,读取对象p,并打印出来。

    // 创建一个ObjectInputStream对象,传入一个输入流作为参数val ois = new ObjectInputStream(new FileInputStream("./data/4.txt"))// 调用readObject方法,从输入流中读取对象,并转换为Person类型var p: Person = ois.readObject().asInstanceOf[Person]// 打印对象pprintln(p)// 关闭输入流ois.close()

    3.4 使用注解

    除了使用Serializable特质之外,还可以使用@SerialVersionUID注解来指定一个版本号。这样可以避免不同版本的类之间的兼容性问题。例如,以下代码定义了一个普通的类,并添加了注解:

    import java.io._// 使用@SerialVersionUID注解指定版本号为100L@SerialVersionUID(100L)// 定义一个普通的类,并继承Serializable特质class Person(var name: String, var age: Int) extends Serializable

    然后,可以使用相同的方式进行序列化和反序列化操作:

    val p = new Person("张三", 23)val oos = new ObjectOutputStream(new FileOutputStream("./data/4.txt"))oos.writeObject(p)oos.close()val ois = new ObjectInputStream(new FileInputStream("./data/4.txt"))var p: Person = ois.readObject().asInstanceOf[Person]println(p)

    案例-学员成绩表

    概述

    已知项目下的data文件夹的student.txt文本文件中, 记录了一些学员的成绩, 如下: 格式为: 姓名 语文成绩 数学成绩 英语成绩

    张三 37 90 100
    李四 90 73 81
    王五 60 90 76
    赵六 89 21 72
    田七 100 100 100

    按照学员的总成绩降序排列后, 按照 姓名 语文成绩 数学成绩 英语成绩 总成绩 的格式, 将数据写到项目下的data文件夹的stu.txt文件中.

    ** 步骤**

    • 定义样例类Person, 属性为: 姓名, 语文成绩, 数学成绩, 英语成绩, 且该类中有一个获取总成绩的方法.

    • 读取指定文件(./data/student.txt)中所有的数据, 并将其封装到List列表中.

    • 定义可变的列表ListBuffer[Student], 用来记录所有学生的信息.

    • 遍历第二步获取到的数据, 将其封装成Person类的对象后, 并添加到ListBuffer中.

    • 对第4步获取到的数据进行排序操作, 并将其转换成List列表.

    • 按照指定格式, 通过BufferWriter将排序后的数据写入到目的地文件中(./data/stu.txt)

    • 关闭流对象.

    参考代码

    object ClassDemo08 {//1. 定义样例类Person, 属性: 姓名, 语文成绩, 数学成绩, 英语成绩, 且该类中有一个获取总成绩的方法.case class Student(name:String, chinese:Int, math:Int, english:Int) {def getSum() = chinese + math + english}def main(args: Array[String]): Unit = {//2. 获取数据源文件对象.val source = Source.fromFile("./data/student.txt")//3. 读取指定文件(./data/student.txt)中所有的数据, 并将其封装到List列表中.var studentList: Iterator[List[String]] = source.getLines().map(_.split(" ")).map(_.toList)//4. 定义可变的列表ListBuffer[Student], 用来记录所有学生的信息.val list = new ListBuffer[Student]()//5. 遍历第二步获取到的数据, 将其封装成Person类的对象后, 并添加到ListBuffer中.for(s <- studentList) {list += Student(s(0), s(1).toInt, s(2).toInt, s(3).toInt)}//6. 对第5步获取到的数据进行排序操作, 并将其转换成List列表.val sortList = list.sortBy(_.getSum()).reverse.toList//7. 按照指定格式, 通过BufferWriter将排序后的数据写入到目的地文件(./data/stu.txt)val bw = new BufferedWriter(new FileWriter("./data/stu.txt"))for(s <- sortList) bw.write(s"${s.name} ${s.chinese} ${s.math} ${s.english}${s.getSum()}\r\n")//8. 关闭流对象bw.close()}}

    “Scala文件操作方法有哪些”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

    --结束END--

    本文标题: Scala文件操作方法有哪些

    本文链接: https://www.lsjlt.com/news/355392.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • Scala文件操作方法有哪些
      本篇内容介绍了“Scala文件操作方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 读取数据在Scala语言的  So...
      99+
      2023-07-05
    • PS文件操作的方法有哪些
      本文小编为大家详细介绍“PS文件操作的方法有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“PS文件操作的方法有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。文件操作读到...
      99+
      2024-04-02
    • java文件操作的方法有哪些
      这篇文章主要讲解了“java文件操作的方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java文件操作的方法有哪些”吧!文件的建立/检查与删除<%@ page="&q...
      99+
      2023-06-03
    • python中有哪些文件操作方法
      这篇文章给大家介绍python中有哪些文件操作方法,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。python的数据类型有哪些python的数据类型:1. 数字类型,包括int(整型)、long(长整型)和float(浮...
      99+
      2023-06-14
    • Python中的文件操作方法有哪些
      这篇文章主要介绍“Python中的文件操作方法有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Python中的文件操作方法有哪些”文章能帮助大家解决问题。一、文件的编码计算机中有许多可用编码:U...
      99+
      2023-07-04
    • python文件常用操作方法有哪些
      文件的一般操作步骤打开文件或创建新文件:使用相应的编程语言和对应的库或模块打开一个已经存在的文件或者创建新文件。读取文件内容或向文件中写入内容:通过读取文件内容或向文件中写入内容来实现对文件的操作,可以使用相应的方法和函数来实现。处理文件数...
      99+
      2023-05-20
      Python
    • Python操作Excel文件的方法有哪些
      本篇内容介绍了“Python操作Excel文件的方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!数据处理是 Python 的一大应用...
      99+
      2023-06-16
    • C语言中文件操作方法有哪些
      这篇文章主要介绍了C语言中文件操作方法有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。什么是文件程序文件其实从文件的功能的角度来说:我们的文件可以分为两种类型的文件:程序...
      99+
      2023-06-25
    • Nodejs读写文件的操作方法有哪些
      这篇“Nodejs读写文件的操作方法有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Nodejs读写文件的操作方法有哪些...
      99+
      2023-07-04
    • C语言文件的操作方法有哪些
      这篇文章主要讲解了“C语言文件的操作方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言文件的操作方法有哪些”吧!文件的顺序读写字符输入输出fgetc和fputcfgetc:字符输...
      99+
      2023-06-29
    • Node.js进行文件操作的方法有哪些
      这篇文章主要介绍“Node.js进行文件操作的方法有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node.js进行文件操作的方法有哪些”文章能帮助大家解决问题。Node.js是一个基于Chro...
      99+
      2023-07-05
    • VB.NET文件操作语法有哪些
      这篇文章主要介绍VB.NET文件操作语法有哪些,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.拷贝文件(FileCopy语句)格式:FileCopy(源文件名),(目标文件名)功能:可以把源文件拷贝到目标文件,拷贝...
      99+
      2023-06-17
    • java实现操作文件的方法有哪些
      这篇文章将为大家详细讲解有关java实现操作文件的方法有哪些,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Java7中文件IO发生了很大的变化,专门引入了很多新的类:import java....
      99+
      2023-05-31
      java 文件操作
    • python文件处理的操作方法有哪些
      Python文件处理的操作方法有以下几种: 打开文件:使用内置的open()函数来打开一个文件。可以指定文件路径、打开模式(读取、...
      99+
      2024-02-29
      python
    • jQuery文档操作方法有哪些
      jQuery提供了一系列的文档操作方法,包括但不限于以下几种:1. .html():获取或设置被选元素的HTML内容。2. .tex...
      99+
      2023-10-11
      jQuery
    • java中常用的文件操作方法有哪些
      这篇文章将为大家详细讲解有关java中常用的文件操作方法有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。常用的文件的操作方法public class FileUtility { public stat...
      99+
      2023-06-03
    • Python操作CSV格式文件的方法有哪些
      这篇文章主要介绍“Python操作CSV格式文件的方法有哪些”,在日常操作中,相信很多人在Python操作CSV格式文件的方法有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python操作CSV格式文件...
      99+
      2023-06-20
    • bat文件与Vbs文件间的常用操作方法有哪些
      这篇“bat文件与Vbs文件间的常用操作方法有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“bat文件与Vbs文件间的常...
      99+
      2023-07-04
    • c语言文件读写的操作方法有哪些
      本篇内容介绍了“c语言文件读写的操作方法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!c语言的文件读写操作:1、用fgetc()和fp...
      99+
      2023-07-04
    • Python对PDF文件的常用操作方法有哪些
      这篇文章主要介绍“Python对PDF文件的常用操作方法有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Python对PDF文件的常用操作方法有哪些”文章能帮助大家解决问题。工具python3....
      99+
      2023-06-30
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作