我从postgresql表导出了多个字段,包括boolean(由postgresql导出为t
和f
字符),我需要将其导入另一个赢得的数据库(monetdb)将t / f理解为bool值。
(编辑删除空格以反映真实的文件方面并避免愤怒的评论 - 之前显示的空格)
id|val_str|bool_1|bool2|bool_3|bool4|
1|help|t|t|f|t|
2|test|f|t|f|f|
...
由于我无法替换t
/ f
的所有出现,我需要在我的模式中集成字段分隔符。
我尝试使用awk
将字段t
替换为TRUE
,将f
替换为FALSE
:
awk -F'|' '{gsub(/\|t\|/, "|TRUE|"); gsub(/\|f\|/, "|FALSE|"); print;}'
这是部分工作,因为具有相同值(|t|t|
)的连续字段将仅替换第一个匹配项(|TRUE|t|
- 因为第二次出现实际上是t|
而不是{ {1}})。
|t|
表有~450列,所以我无法真正指定要替换的列列表,也不能在postgres中使用'转换'布尔列(我可以......)。
我可以运行id|val_str|bool_1|bool2|bool_3|bool4|
1|help|TRUE|t|FALSE|TRUE|
2|test|FALSE|TRUE|FALSE|f|
...
两次,但我正在寻找更优雅的方式来匹配所有字段的整个字段内容。
gsub()
没有任何帮助,因为我们大多数时间都处于中间位置。
答案 0 :(得分:3)
表有~450列,所以我无法真正指定列的列表 要被替换,也不能用postgres来'变换'布尔列(I 可以,但......)。
您可以让Postgres为您完成工作。生成SELECT
列表的基本查询:
SELECT string_agg(CASE WHEN atttypid = 'bool'::regtype
THEN quote_ident(attname) || '::text'
ELSE quote_ident(attname) END, ', ' ORDER BY attnum)
FROM pg_attribute
WHERE attrelid = 'mytable'::regclass -- provide table name here
AND attnum > 0
AND NOT attisdropped;
生成以下形式的字符串:
col1, "CoL 2", bool1::text, "Bool 2"::text
所有标识符都已正确转义。列是默认顺序。复制并执行它。使用COPY
导出到文件。 (或者psql中的\copy
。)性能与导出普通表大致相同。如果您不需要大写,请省略upper()
。
为什么简单的转换为text
?
关于regclass
并正确转义标识符:
如果您需要一个包含TRUE
/ FALSE
/ NULL
大写的完整语句,标准SQL强制表示法(不带冒号::
),仍然是原始列名和也许是一个模式限定的表名:
SELECT 'SELECT '
|| string_agg(CASE WHEN atttypid = 'bool'::regtype
THEN format('upper(cast(%1$I AS text)) AS %1$I', attname)
ELSE quote_ident(attname) END, ', ' ORDER BY attnum)
|| ' FROM myschema.mytable;' -- provide table name twice now
FROM pg_attribute
WHERE attrelid = 'myschema.mytable'::regclass
AND attnum > 0
AND NOT attisdropped;
生成表格的完整声明:
SELECT col1, "CoL 2", upper(cast(bool1 AS text) AS bool1, upper(cast("Bool 2" AS text)) AS "Bool 2" FROM myschema.mytable;
答案 1 :(得分:1)
如果perl
没问题,可以使用lookarounds:
$ cat ip.txt
id | val_str | bool_1 | bool2 | bool_3 | bool4 |
1 | help | t | t | f | t |
2 | test | f | t | f | f |
$ perl -pe 's/\|\K\h*t\h*(?=\|)/ TRUE /g; s/\|\K\h*f\h*(?=\|)/ FALSE /g' ip.txt
id | val_str | bool_1 | bool2 | bool_3 | bool4 |
1 | help | TRUE | TRUE | FALSE | TRUE |
2 | test | FALSE | TRUE | FALSE | FALSE |
\|\K
正面看后来匹配|
\h*
可选的水平空格,如果输入中实际不存在则删除(?=\|)
肯定前瞻以匹配|
也可以使用sed
循环。在GNU sed 4.2.2
上测试,语法可能会因其他实现而异[/ p>
$ sed ':a s/| *t *|/| TRUE |/;ta; :b s/| *f *|/| FALSE |/;tb' ip.txt
id | val_str | bool_1 | bool2 | bool_3 | bool4 |
1 | help | TRUE | TRUE | FALSE | TRUE |
2 | test | FALSE | TRUE | FALSE | FALSE |
:a
标签s/| *t *|/| TRUE |/
替换命令ta
只要替换命令成功a
:b
输入中没有空格
perl -pe 's/\|\Kt(?=\|)/TRUE/g; s/\|\Kf(?=\|)/FALSE/g' ip.txt
sed ':a s/|t|/|TRUE|/;ta; :b s/|f|/|FALSE|/;tb' ip.txt
awk 'BEGIN{FS=OFS="|"} {for(i=1;i<=NF;i++){if($i=="t"){$i="TRUE"} if($i=="f"){$i="FALSE"}} print}' ip.txt
答案 2 :(得分:1)
假设(根据您的评论)您的输入文件实际上看起来不像您发布的样本,而是看起来像这样:
$ cat file
id|val_str|bool_1|bool2|bool_3|bool4|
1|help|t|t|f|t|
2|test|f|t|f|f|
然后您只需要:
$ awk '{while(gsub(/\|t\|/,"|TRUE|")); while(gsub(/\|f\|/,"|FALSE|"));}1' file
id|val_str|bool_1|bool2|bool_3|bool4|
1|help|TRUE|TRUE|FALSE|TRUE|
2|test|FALSE|TRUE|FALSE|FALSE|
N替换字符串的一般解决方案是:
$ awk 'BEGIN{m["f"]="FALSE"; m["t"]="TRUE"} {for (k in m) while(gsub("\\|"k"\\|","|"m[k]"|"));} 1' file
id|val_str|bool_1|bool2|bool_3|bool4|
1|help|TRUE|TRUE|FALSE|TRUE|
2|test|FALSE|TRUE|FALSE|FALSE|
答案 3 :(得分:0)
使用sed
,这是标准。
sed 's/| *t */| TRUE /g;s/| *f */| FALSE /g'
这告诉sed
替换以管道字符,未知数量的空格(可能为零),t
和空格后跟未知空格数{{1}的每个子字符串。 }};与| TRUE
相同。
如果线路长度混乱,则通过f
管道输出。