CSVReader

文档中心 › 组件索引 › CSVReader

CSVReader Controller Service

类型全限定名:cn.org.acal.lynxus.csv.CSVReader

所属 Bundle:cn.org.acal.lynxus:lynxus-record-serialization-services-nar:2.11.8

描述

解析 CSV 格式的数据,将 CSV 文件中的每一行作为单独的记录返回。 如果存在“标题行”,则此读取器允许根据 CSV 的第一行推断模式,或者提供用于解释值的显式模式。 请参阅Controller Service的使用以获取更多文档。

标签

csvparserecordrowreaderdelimitedcommaseparatedvalues

属性

下表中,加粗的属性为必填项;其余为可选项。表格同时标注默认值、是否支持表达式语言,以及是否为敏感属性(值会被加密)。

名称默认值可选值描述
Schema Access Strategyinfer-schema
  • 使用 'Schema Name' 属性 — 要使用的 Schema 名称由 'Schema Name' 属性指定。此属性的值用于在配置的 Schema Registry 服务中查找 Schema。
  • 使用 'Schema Text' 属性 — Schema 本身的文本由 'Schema Text' 属性指定。此属性的值必须是有效的 Avro Schema。如果使用表达式语言,则 'Schema Text' 属性的值在替换表达式后必须有效。
  • Schema 引用读取器 — Schema 引用信息将通过配置的 Schema 引用读取器服务实现提供。
  • 使用标题中的字符串字段 — CSV 文件的第一个非注释行是包含列名的标题行。Schema 将使用标题中的列名派生,并假设所有列都是 String 类型。
  • 推断 Schema — 在读取数据时将自动推断数据的 Schema。有关如何推断 Schema 的信息,请参阅组件的使用说明和其他详细信息。
指定如何获取用于解释数据的架构。
Schema Registry控制器服务 API:
SchemaRegistry
cn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8
指定用于架构Registry的Controller Service
Schema Name${schema.name}指定要在架构Registry属性中查找的架构名称
支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值)
Schema Version指定在 Schema Registry 中查找 Schema 的版本。如果未指定,则检索 Schema 的最新版本。
支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值)
Schema Branch指定在 Schema Registry 属性中查找 Schema 时使用的分支名称。如果选择的 Schema Registry 不支持分支,则此值将被忽略。
支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值)
Schema Text${avro.schema}Avro 格式的架构文本
支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值)
Schema Reference Reader控制器服务 API:
SchemaReferenceReader
cn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8
服务实现负责读取 FlowFile 属性或内容以确定架构参考标识符
CSV Parsercommons-csv
  • Apache Commons CSV — 来自 Apache Commons CSV 库的 CSV 解析器实现。
  • Jackson CSV — 来自 Jackson Dataformats 库的 CSV 解析器实现。
  • FastCSV — 来自 FastCSV 库的 CSV 解析器实现。注意:此解析器仅官方支持 RFC-4180,因此建议将 'CSV Format' 属性设置为 'RFC 4180'。它确实可以处理一些不合规的 CSV 数据,对于这种情况,请将 'CSV Format' 属性设置为 'CUSTOM',并相应地设置其他自定义格式属性(如 'Trim Fields'、'Trim double quote' 等)。请注意,如果 FastCSV 无法正确处理属性设置(如 'Ignore Header'),可能会导致错误,否则即使数据不完全符合 RFC-4180,也可能按预期处理输入。
指定使用哪个解析器来读取 CSV 记录。注意:不同的解析器可能支持不同的功能子集,并且可能表现出不同的性能水平。
Date Format指定读取/写入日期字段时使用的格式。如果未指定,日期字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,MM/dd/yyyy 表示两位数字的月份,后跟两位数字的日期,后跟四位数字的年份,全部以 '/' 字符分隔,如 01/01/2017)。
Time Format指定读取/写入时间字段时使用的格式。如果未指定,时间字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,HH:mm:ss 表示 24 小时格式的两位数字的小时,后跟两位数字的分钟,后跟两位数字的秒,全部以 ':' 字符分隔,如 18:04:15)。
Timestamp Format指定读取/写入时间戳字段时使用的格式。如果未指定,时间戳字段将被视为自纪元(1970 年 1 月 1 日午夜 GMT)以来的毫秒数。如果指定,值必须匹配 Java java.time.format.DateTimeFormatter 格式(例如,MM/dd/yyyy HH:mm:ss 表示两位数字的月份,后跟两位数字的日期,后跟四位数字的年份,全部以 '/' 字符分隔;然后后跟 24 小时格式的两位数字的小时,后跟两位数字的分钟,后跟两位数字的秒,全部以 ':' 字符分隔,如 01/01/2017 18:04:15)。
CSV 格式custom
  • 自定义格式 — CSV 的格式是通过使用此Controller Service的属性来配置的,例如值分隔符
  • RFC 4180 — CSV 数据遵循 https://tools.ietf.org/html/rfc4180 中定义的 RFC 4180 规范
  • Microsoft Excel — CSV 数据遵循 Microsoft Excel 使用的格式
  • 制表符分隔 — CSV 数据是制表符分隔而不是逗号分隔
  • MySQL 格式 — CSV 数据遵循 MySQL 使用的格式
  • Informix Unload — Informix 在执行 UNLOAD TO file_name 命令时使用的格式
  • Informix Unload 禁用转义 — Informix 在执行 UNLOAD TO file_name 命令时使用的格式,且禁用转义
指定 CSV 数据使用的 "格式",或指定是否应使用自定义格式。
Value Separator,用于在 CSV 记录中分隔值/字段的字符。如果通过 Expression Language 指定了此属性,但在运行时求值为无效的值分隔符,则将跳过该值并使用默认的值分隔符。
支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值)
Record Separator\n指定用于分隔 CSV 记录的字符
Treat First Line as Headerfalse
  • true
  • false
指定是否将 CSV 的第一行视为表头或视为记录。如果 Schema 访问策略指示必须在表头中定义列,则将忽略此属性,因为表头必须始终存在且不会被作为记录处理。否则,如果为 'true',则 CSV 数据的第一行不会被作为记录处理;如果为 'false',则第一行将被解释为记录。
Ignore CSV Header Column Namesfalse
  • true
  • false
如果 CSV 的第一行是表头,且配置的 Schema 与表头行中的字段名不匹配,此属性控制 Reader 如何解释这些字段。如果此属性为 true,则映射到每列的字段名仅由配置的 Schema 决定,不在 Schema 中的字段将被忽略。如果此属性为 false,则将使用 CSV 表头中的字段名作为字段名。
Quote Character"用于引用值的引号字符,这样就不必使用转义字符。如果通过 Expression Language 指定了此属性,但在运行时求值为无效的引号字符,则将跳过该值并使用默认的引号字符。
支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值)
Escape Character\用于转义在 CSV 解析器中具有特殊含义的字符的转义字符。如果通过 Expression Language 指定了此属性,但在运行时求值为无效的转义字符,则将跳过该值并使用默认的转义字符。设置为空字符串表示不使用转义字符。
支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值)
Comment Marker用于表示注释开始的字符。 任何以此注释开头的行都将被忽略。
支持表达式语言:是(使用 FlowFile 属性与环境/系统变量求值)
Null String指定一个字符串,如果该字符串作为值出现在 CSV 中,则应将其视为空字段,而不是使用文字值。
Trim Fieldstrue
  • true
  • false
是否应删除字段开头和结尾的空格
Character SetUTF-8用于对 CSV 文件进行编码/解码的字符编码
Allow Duplicate Header Namestrue
  • true
  • false
是否允许重复的表头名称。表头名称区分大小写,例如 "name" 和 "Name" 被视为不同的字段。 重复表头名称的处理方式取决于具体的 CSV 解析器(在适用情况下): * Apache Commons CSV - 重复表头会导致列数据向右"移动",并为 "unknown_field_index_X" 创建新字段,其中 "X" 是 CSV 列索引号 * Jackson CSV - 重复表头将被去重,字段值取最右侧重复 CSV 列的值 * FastCSV - 重复表头将被去重,字段值取最左侧重复 CSV 列的值
Trim Double Quotetrue
  • true
  • false
是否修剪起始和结尾的双引号。例如:使用 trim 时,字符串 '"test"' 将被解析为 'test',不使用 trim 时将被解析为 '"test"'。如果设置为 'false',则表示完全符合 RFC-4180。默认值为 true,使用 trim。

状态管理

该组件不存储状态。

受限 (Restricted)

该组件不受限。

提供的 Service API

  • RecordReaderFactory (cn.org.acal.lynxus-lynxus-standard-services-api-nar-2.11.8)

详细说明

CSVReader

CSVReader 可以把输入数据解析为带分隔符的记录(Record)。默认使用逗号作为 字段分隔符,但这是可配置的。例如,使用制表符来读取以制表符分隔的值(TSV) 是一种常见做法。 Reader 内置了预定义的 CSV 格式(如 EXCEL)。关于这些格式配置的更多信息 参见:https://commons.apache.org/proper/commons-csv/apidocs/org/apache/commons/csv/CSVFormat.html Reader 允许自定义 CSV 格式,例如用什么字符分隔 CSV 字段、用什么字符做 引号以及何时对字段加引号、用什么字符表示注释等。字段名的指定方式有两种: 以"表头行(header line)"作为 CSV 的第一行(此时 Schema Access Strategy 应为 "Infer Schema" 或 "Use String Fields From Header"),或者通过 Schema Text 指定、或在 Schema Registry 中查找 schema 来提供。

Schema 与类型强制转换

从传入数据解析记录时,数据会被拆分为各个字段。每个字段随后会按字段名与 配置的 schema 进行比对,以确定数据的类型。如果字段不在 schema 中,该字段 会从记录中省略。如果在 schema 中找到了该字段,接收到的数据的数据类型会 与 schema 指定的数据类型比较。如果类型一致,字段值原样使用。如果 schema 表明该字段应为其他类型,Controller Service 会尝试把数据强制转换为 schema 指定的类型。如果无法强制转换,会抛出异常。

尝试把一个字段值从一种数据类型强制转换为另一种时,适用以下规则:

  • 任何数据类型都可以强制转换为 String 类型。
  • 任何数值类型(Byte、Short、Int、Long、Float、Double)都可以强制转换为其他数值类型。
  • 任何数值都可以强制转换为 Date、Time 或 Timestamp 类型,方式是把 Long 值 视为自纪元(1970 年 1 月 1 日午夜,GMT)以来的毫秒数。
  • 如果 String 值的格式与配置的 "Date Format"、"Time Format" 或 "Timestamp Format" 匹配,就可以强制转换为 Date、Time 或 Timestamp 类型。
  • 如果值的类型合适,String 值可以强制转换为数值。例如,String 值 8 可以强制转换为任何数值类型;但 String 值 8.2 只能强制转换为 Double 或 Float 类型,不能转为 Integer。
  • String 值 "true" 或 "false"(不区分大小写)可以强制转换为 Boolean 值。
  • 非空的 String 值可以强制转换为 Char 类型。如果字符串包含多个字符, 只使用第一个字符,其余忽略。
  • 任何"日期/时间"类型(Date、Time、Timestamp)都可以强制转换为其他"日期/时间"类型。
  • 任何"日期/时间"类型都可以强制转换为 Long 类型,表示自纪元(1970 年 1 月 1 日午夜,GMT)以来的毫秒数。
  • 任何"日期/时间"类型都可以强制转换为 String。String 的格式就是对应属性 (Date Format、Time Format、Timestamp Format)所配置的 DateFormat。

如果尝试在两种数据类型之间强制转换时上述规则都不适用,转换会失败并抛出异常。

Schema 推断

虽然 Lynxus 的 Record API 要求每条记录都有 schema,但基于数据中的值来推断 schema 往往比手动创建更方便。做法是把 "Schema Access Strategy" 属性设为 "Infer Schema"。使用该策略时,Reader 会先解析 FlowFile 中的所有数据, 记录遇到的所有字段及每个字段的类型。所有数据解析完成后,会形成一个涵盖 所有已遇到字段的 schema。

推断 schema 时的一个常见顾虑是如何处理两个值类型不同的情况。例如,考虑 包含以下两条记录的 FlowFile:

name, age
John, 8
Jane, Ten

显然 "name" 字段会被推断为 STRING 类型。但 "age" 字段该如何处理?应该是 INT 和 STRING 之间的 CHOICE 吗?应该优先用 LONG 而不是 INT 吗?直接用 STRING 吗?该字段应视为可为 null 吗?

为帮助理解该 Record Reader 如何推断 schema,以下是推断逻辑所遵循的规则:

  • 所有字段都被推断为可为 null。
  • 当两条不同记录中同一字段遇到两个值(或 ARRAY 类型遇到两个值)时, 推断引擎倾向于使用"更宽"的数据类型,而不是 CHOICE 类型。当且仅当数据 类型 "A" 除了包含 "B" 的所有值外还包含其他值时,才称 "A" 比 "B" 宽。 例如,LONG 类型比 INT 宽,但不比 BOOLEAN 宽(BOOLEAN 也不比 LONG 宽)。 INT 比 SHORT 宽。STRING 类型被认为比除 MAP、RECORD、ARRAY、CHOICE 之外的所有类型都宽。
  • 在推断值的类型之前,会先去掉首尾空白。此外,如果值被双引号(")包围, 双引号会被去掉。因此,值 16 与 "16" 的解释方式相同,都会被解释为 INT。但值 " 16" 会被推断为 STRING 类型,因为空白被包在双引号内, 意味着空白被视为值的一部分。
  • 如果配置了 "Time Format"、"Timestamp Format" 或 "Date Format" 属性, 任何原本会被视为 STRING 类型的值,都会先与配置的格式比对,看是否匹配。 如果值匹配 Timestamp Format,视为 Timestamp 字段;匹配 Date Format 视为 Date 字段;匹配 Time Format 视为 Time 字段。如果值碰巧匹配多个 配置的格式,按 Timestamp、Date、Time 的顺序匹配。也就是说,如果一个值 同时匹配 Timestamp Format 和 Date Format,推断出的类型是 Timestamp。 由于解析日期和时间开销较大,如果不预期出现日期、时间和时间戳,或者可以 接受按 STRING 处理,建议不要配置这些格式。对于需要此功能的用例, 推断引擎足够智能,会先检查若干非常廉价的条件来优化解析。例如,先检查 字符串长度是否过长或过短、不可能匹配模式。这比尝试把每个字符串值都 解析为时间戳要高效得多。
  • MAP 类型永远不会被推断。
  • ARRAY 类型永远不会被推断。
  • RECORD 类型永远不会被推断。
  • 如果字段存在但所有值都是 null,该字段被推断为 STRING 类型。

推断 Schema 的缓存

该 Record Reader 要求:如果要推断 schema,必须读取所有记录,以确保推断出的 schema 适用于 FlowFile 中的所有记录。但这可能开销很大,尤其是数据要经过 很多不同转换时。为降低推断成本,可以通过设置同名属性为该 Record Reader 配置 "Schema Inference Cache"。这是一个可以在 Record Reader 和 Record Writer 之间共享的 Controller Service。

每当使用 Record Writer 写出数据时,如果它配置了 "Schema Cache",也会把 schema 添加到 Schema Cache 中。这会使该 schema 的标识符作为 Attribute 添加到 FlowFile 上。

每当使用 Record Reader 读取数据时,如果它配置了 "Schema Inference Cache", 会先在 FlowFile 上查找 "schema.cache.identifier" Attribute。如果该 Attribute 存在,就用它的值在 schema 缓存中查找 schema。如果能在缓存中 找到该标识符对应的 schema,就直接使用,而不必读取、解析和分析数据来 推断。如果 FlowFile 上没有该 Attribute,或缓存中没有对应标识符的 schema,Record Reader 会按上述方式推断。

最终效果是:用户可以串联许多不同的 Processor 来处理面向 Record 的数据。 通常只有链中的第一个 Processor 需要承担推断 schema 的"代价"。对链中的 其他所有 Processor,Record Reader 可以直接按标识符在 Schema Cache 中 查找。这使得 Record Reader 既能准确推断(基于 FlowFile 中的所有数据), 又能高效执行(无论多少 Processor 处理数据,通常只推断一次)。

示例

示例 1

举例来说,考虑一个内容如下、带注释的 FlowFile:

id, name, balance, join_date, notes
1, John, 48.23, 04/03/2007, "Our very   first customer!"
2, Jane, 1245.89, 08/22/2009,	  
3, Frank Franklin, "48481.29", 04/04/2016,  

再假设该 Controller Service 配置的 Schema Registry 指向 AvroSchemaRegistry,schema 配置如下:

{
  "namespace": "lynxus",
  "name": "balances",
  "type": "record",
  "fields": [
    {
      "name": "id",
      "type": "int"
    },
    {
      "name": "name",
      "type": "string"
    },
    {
      "name": "balance",
      "type": "double"
    },
    {
      "name": "join_date",
      "type": {
        "type": "int",
        "logicalType": "date"
      }
    },
    {
      "name": "notes",
      "type": "string"
    }
  ]
}

在上面的例子中,'join_date' 列是 Date 类型。为了让 CSV Reader 能够正确 地把值解析为日期,需要为 Reader 提供要使用的日期格式。本例中,我们把 Date Format 属性配置为 MM/dd/yyyy,表示两位月份、两位日期、四位年份, 以斜杠分隔。此时,该 FlowFile 会生成 3 条不同的记录。第一条记录包含 以下值:

字段名 字段值
id 1
name John
balance 48.23
join_date 04/03/2007
notes Our very
first customer!

第二条记录包含以下值:

字段名 字段值
id 2
name Jane
balance 1245.89
join_date 08/22/2009
notes

第三条记录包含以下值:

字段名 字段值
id 3
name Frank Franklin
balance 48481.29
join_date 04/04/2016
notes

示例 2 - 带 CSV 表头行的 Schema

当 CSV 数据包含描述列名的表头行时,Reader 提供了若干属性来配置如何处理 这些列名。"Schema Access Strategy" 属性及相关属性("Schema Registry"、 "Schema Text" 和 "Schema Name")可用于指定 schema 的获取方式。如果 "Schema Access Strategy" 设为 "Use String Fields From Header",CSV 的 表头行会被用来确定 schema;否则 schema 从其他地方引用。但如果从 Schema Registry 获取的 schema 与 CSV 表头指示的列名不一致,会发生什么?

例如,假设从 Schema Registry 获取了以下 schema:

{
  "namespace": "lynxus",
  "name": "balances",
  "type": "record",
  "fields": [
    {
      "name": "id",
      "type": "int"
    },
    {
      "name": "name",
      "type": "string"
    },
    {
      "name": "balance",
      "type": "double"
    },
    {
      "name": "memo",
      "type": "string"
    }
  ]
}

而 CSV 包含以下数据:

id, name, balance, notes 1, John Doe, 123.45, First Customer

注意,schema 表明最后一列名为 "memo",而 CSV 表头指示的名称是 "notes"。

此时,Reader 会查看 "Ignore CSV Header Column Names" 属性。如果该属性 设为 "true",CSV 中提供的列名会被直接忽略,最后一列将名为 "memo"。 但如果 "Ignore CSV Header Column Names" 属性设为 "false",结果是最后 一列名为 "notes",每条记录的 "memo" 列都是 null 值。

"Ignore CSV Header Column Names" 属性设为 "false" 时:

字段名 字段值
id 1
name John Doe
balance 123.45
memo First Customer

"Ignore CSV Header Column Names" 属性设为 "true" 时:

字段名 字段值
id 1
name John Doe
balance 123.45
notes First Customer