如何编写 SSIS 表达式以从完全限定的文件名中提取一个文件夹名称
How do I write an SSIS Expression to extract one folder name from a fully qualified file name
我有一个带有 ForEach 文件枚举循环(完全限定名称)的 SSIS 程序包,其中包含一个 FTP 任务。
包执行时会遍历以下目录子文件夹中的文件
C:\Test\Test2\ABC\
*.txt
并且它将 post 文件发送到 FTP 站点。
我在 foreach 循环中定义了一个名为@[User::Filename] 的变量。
但是 FTP 中有文件夹,我希望根据文件从 C 驱动器上获取的文件夹转到这些文件。
C:\Test\Test2\ABC\A\1.txt
C:\Test\Test2\ABC\B\2.txt
C:\Test\Test2\ABC\C\3.txt
文件 1.txt 应转到名为 \FTP\A
的 FTP 文件夹
文件 2.txt 应转到名为 \FTP\B
的 FTP 文件夹
文件 3.txt 应该转到名为 \FTP\C
的 FTP 文件夹
我最初的想法是让远程路径成为一个变量,并从 foreach 循环变量中获取完全限定的名称。
为此,我创建了一个名为@[User::FilenameFTP] 的变量,并将以下内容输入到表达式
"//FTP//" +
RIGHT(
(LEFT(@[User::Filename], ABS((FINDSTRING(@[User::Filename], "//", 5)))),
ABS((FINDSTRING(@[User::Filename], "//", 5)-1)) - ABS((FINDSTRING(@[User::Filename], "//",4)+1))
)
我认为这个公式会给出文件所在的 C 驱动器中的文件名,我将其用作 FTP 任务中的远程路径变量。但是当我 运行 它时,文件仍然进入 \FTP\ 而不是进入子文件夹。
我 运行 对此有一个脚本任务,但输出也没有显示我想要的内容。我究竟做错了什么?这不能通过这种方式在 foreach 循环中编辑变量吗?
如果您的驱动器名称(或多或少)如您所显示的那样出现,那么它们应该是反斜杠 ("\"
) 而不是表达式中的正斜杠。可能不是问题,但我改变了它们来解决这个问题。
使用 C
文件夹字符串,在所写的表达式中,ABS((FINDSTRING(@[User::Filename], "\", 5)-1))
和 ABS((FINDSTRING(@[User::Filename], "\",4)+1))
的计算结果均为 19,因此表达式归结为 RIGHT(<<String>>,0)
,并且,来自文档,If integer_expression is zero, the function returns a zero-length string.。因此,您没有在 FTP 基本文件夹名称的末尾附加任何内容。
肮脏的修正
我们可能会把所有 LEFT
和 RIGHT
和 FINDSTRING
搞得一团糟,但是如果您知道您要查找的文件夹名称将始终是你的完全限定名称(你的表达式已经依赖于它)你可以使用 TOKEN
更快地到达那里,并指定你的斜线分隔字符串的第五个元素:
"//FTP//" + TOKEN( @[User::Filename],"\",5) +"//"
计算结果为 //FTP//C//
。
更可持续的修复
另一方面,如果您想让您的代码适应未来的需要,在您添加或删除文件夹层次结构级别的那一天,我建议提取最后一个文件夹名称,而不考虑多少层文件夹在前。
我们可以使用 SUBSTRING
和一些聪明的 REVERSE
工作来做到这一点,感谢 KeithL for ,这让我兴奋不已。
SUBSTRING
接受三个参数。我们有字符串 @[User::Filename]
,这就是一个。第二个是从字符串左端开始的位置,第三个是要提取的字符数。
要获得起始位置,我们将使用 REVERSE
找到倒数第二个斜杠的位置,从字符串的右端计算字符数:
FINDSTRING(REVERSE( @[User::Filename]),"\",2) (evaluates to 8 here)
所以我们的起始位置是字符串的总长度减去倒数第二个斜杠后面的字符数。
LEN( @[User::Filename]) - FINDSTRING(REVERSE( @[User::Filename]),"\",2) (=17)
我们可以通过从倒数第二个斜线的反向位置减去最后一个斜线的反向位置,然后再减去一个,来获得要拉出的字符数,因为我们不希望尾部斜线出现在我们的字符串还没有。
FINDSTRING(REVERSE( @[User::Filename]),"\",2)
- FINDSTRING(REVERSE( @[User::Filename]),"\",1) - 1 (= 1 in our example)
这是我们的三个论点。将所有这些与您的基本文件夹名称放在一起(我添加了一个尾部斜杠。如果这对您不起作用,请将其从那里删除!):
"//FTP//"
+ SUBSTRING(
@[User::Filename] ,
LEN( @[User::Filename]) - FINDSTRING(REVERSE( @[User::Filename]),"\",2),
FINDSTRING(REVERSE( @[User::Filename]),"\",2)
-FINDSTRING(REVERSE( @[User::Filename]),"\",1)-1 )
+ "//"
计算为 //FTP//C//
。
现在,当权力决定“清理”那个源服务器时,Test2
层突然消失,或者事情变得疯狂,而你把这一切埋得更深,您的代码仍然有效。
旁注
如果你真的在你的文件路径名中使用驱动器号,比如 C:\
,请注意当你 运行 你的包在本地时,那是你的 C:\ 驱动包正在使用,但是当你部署它时,它会在 服务器的 C:\ 驱动器上四处寻找,它可能不喜欢它找到的东西,或者更可能不喜欢它找到的东西在那里找到。
我有一个带有 ForEach 文件枚举循环(完全限定名称)的 SSIS 程序包,其中包含一个 FTP 任务。
包执行时会遍历以下目录子文件夹中的文件
C:\Test\Test2\ABC\
*.txt
并且它将 post 文件发送到 FTP 站点。
我在 foreach 循环中定义了一个名为@[User::Filename] 的变量。
但是 FTP 中有文件夹,我希望根据文件从 C 驱动器上获取的文件夹转到这些文件。
C:\Test\Test2\ABC\A\1.txt
C:\Test\Test2\ABC\B\2.txt
C:\Test\Test2\ABC\C\3.txt
文件 1.txt 应转到名为 \FTP\A
的 FTP 文件夹文件 2.txt 应转到名为 \FTP\B
的 FTP 文件夹文件 3.txt 应该转到名为 \FTP\C
的 FTP 文件夹我最初的想法是让远程路径成为一个变量,并从 foreach 循环变量中获取完全限定的名称。
为此,我创建了一个名为@[User::FilenameFTP] 的变量,并将以下内容输入到表达式
"//FTP//" +
RIGHT(
(LEFT(@[User::Filename], ABS((FINDSTRING(@[User::Filename], "//", 5)))),
ABS((FINDSTRING(@[User::Filename], "//", 5)-1)) - ABS((FINDSTRING(@[User::Filename], "//",4)+1))
)
我认为这个公式会给出文件所在的 C 驱动器中的文件名,我将其用作 FTP 任务中的远程路径变量。但是当我 运行 它时,文件仍然进入 \FTP\ 而不是进入子文件夹。
我 运行 对此有一个脚本任务,但输出也没有显示我想要的内容。我究竟做错了什么?这不能通过这种方式在 foreach 循环中编辑变量吗?
如果您的驱动器名称(或多或少)如您所显示的那样出现,那么它们应该是反斜杠 ("\"
) 而不是表达式中的正斜杠。可能不是问题,但我改变了它们来解决这个问题。
使用 C
文件夹字符串,在所写的表达式中,ABS((FINDSTRING(@[User::Filename], "\", 5)-1))
和 ABS((FINDSTRING(@[User::Filename], "\",4)+1))
的计算结果均为 19,因此表达式归结为 RIGHT(<<String>>,0)
,并且,来自文档,If integer_expression is zero, the function returns a zero-length string.。因此,您没有在 FTP 基本文件夹名称的末尾附加任何内容。
肮脏的修正
我们可能会把所有 LEFT
和 RIGHT
和 FINDSTRING
搞得一团糟,但是如果您知道您要查找的文件夹名称将始终是你的完全限定名称(你的表达式已经依赖于它)你可以使用 TOKEN
更快地到达那里,并指定你的斜线分隔字符串的第五个元素:
"//FTP//" + TOKEN( @[User::Filename],"\",5) +"//"
计算结果为 //FTP//C//
。
更可持续的修复
另一方面,如果您想让您的代码适应未来的需要,在您添加或删除文件夹层次结构级别的那一天,我建议提取最后一个文件夹名称,而不考虑多少层文件夹在前。
我们可以使用 SUBSTRING
和一些聪明的 REVERSE
工作来做到这一点,感谢 KeithL for
SUBSTRING
接受三个参数。我们有字符串 @[User::Filename]
,这就是一个。第二个是从字符串左端开始的位置,第三个是要提取的字符数。
要获得起始位置,我们将使用 REVERSE
找到倒数第二个斜杠的位置,从字符串的右端计算字符数:
FINDSTRING(REVERSE( @[User::Filename]),"\",2) (evaluates to 8 here)
所以我们的起始位置是字符串的总长度减去倒数第二个斜杠后面的字符数。
LEN( @[User::Filename]) - FINDSTRING(REVERSE( @[User::Filename]),"\",2) (=17)
我们可以通过从倒数第二个斜线的反向位置减去最后一个斜线的反向位置,然后再减去一个,来获得要拉出的字符数,因为我们不希望尾部斜线出现在我们的字符串还没有。
FINDSTRING(REVERSE( @[User::Filename]),"\",2)
- FINDSTRING(REVERSE( @[User::Filename]),"\",1) - 1 (= 1 in our example)
这是我们的三个论点。将所有这些与您的基本文件夹名称放在一起(我添加了一个尾部斜杠。如果这对您不起作用,请将其从那里删除!):
"//FTP//"
+ SUBSTRING(
@[User::Filename] ,
LEN( @[User::Filename]) - FINDSTRING(REVERSE( @[User::Filename]),"\",2),
FINDSTRING(REVERSE( @[User::Filename]),"\",2)
-FINDSTRING(REVERSE( @[User::Filename]),"\",1)-1 )
+ "//"
计算为 //FTP//C//
。
现在,当权力决定“清理”那个源服务器时,Test2
层突然消失,或者事情变得疯狂,而你把这一切埋得更深,您的代码仍然有效。
旁注
如果你真的在你的文件路径名中使用驱动器号,比如 C:\
,请注意当你 运行 你的包在本地时,那是你的 C:\ 驱动包正在使用,但是当你部署它时,它会在 服务器的 C:\ 驱动器上四处寻找,它可能不喜欢它找到的东西,或者更可能不喜欢它找到的东西在那里找到。