Command line “设置数据文件分隔符”中的多字符分隔符|||";`不';行不通

Command line “设置数据文件分隔符”中的多字符分隔符|||";`不';行不通,command-line,gnuplot,Command Line,Gnuplot,我有一个输入文件example.data,其中三个管道作为分隔符,日期在第一列,还有一些或多或少不可预测的文本在最后一列: 2019-02-01|||123|||345|||567|||Some unpredictable textual data with pipes|, 2019-02-02|||234|||345|||456|||weird symbols @ and commas, and so on. 2019-02-03|||345|||234|||123|||text text t

我有一个输入文件
example.data
,其中三个管道作为分隔符,日期在第一列,还有一些或多或少不可预测的文本在最后一列:

2019-02-01|||123|||345|||567|||Some unpredictable textual data with pipes|,
2019-02-02|||234|||345|||456|||weird symbols @ and commas, and so on.
2019-02-03|||345|||234|||123|||text text text
当我尝试运行下面的gnuplot5脚本时

set terminal png size 400,300
set output 'myplot.png'

set datafile separator "|||"
set xdata time
set timefmt "%Y-%m-%d"
set format x "%y-%m-%d"
plot "example.data" using 1:2 with linespoints
我得到以下错误:

line 8: warning: Skipping data file with no valid points

plot "example.data" using 1:2 with linespoints
                                              ^
"time.gnuplot", line 8: x range is invalid
更奇怪的是,如果我把最后一行改成

plot "example.data" using 1:4 with linespoints
然后它就起作用了。它也适用于
1:7
1:10
,但不适用于其他数字。为什么?

使用

set datafile separator "chars"
在语法中,字符串不被视为一个长分隔符。相反,引号之间列出的每个字符都会单独成为分隔符。自[Janert,2016]:

如果提供显式字符串,则字符串中的每个字符都将 作为分隔符处理

所以,

set datafile separator "|||"
实际上相当于

set datafile separator "|"
还有一条线

2019-02-05|||123|||456|||789
被视为有十列,其中只有列1、4、7、10是非空的


解决方法

查找不太可能出现在数据集中的其他字符(在下面的示例中,我将假定
\t
)。如果无法使用其他分隔符转储数据集,请使用
sed
| |
替换为
\t

sed 's/|||/\t/g' example.data > modified.data # in the command line
然后继续

set datafile separator "\t"

然后
修改。数据作为输入。

基本上你自己给出了答案

  • 如果可以影响数据中的分隔符,请使用通常不会出现在数据或文本中的分隔符。我一直认为
    \t
    就是为了这个

  • 如果无法影响数据中的分隔符,请使用外部工具(awk、Python、Perl等)修改数据。在这些语言中,它可能是“一行”。gnuplot没有直接替换功能

  • 如果您不想安装外部工具,并且希望确保平台独立性,那么仍然可以使用gnuplot来实现。不仅仅是一个“一行程序”,而且对于gnuplot几乎没有什么是不能做的;-)

  • 编辑:使用@Ethan()输入的简化版本

    假设您的数据位于名为
    $data
    的数据集中。以下代码将用
    \t
    替换
    | |
    ,并将结果放入
    $DataOutput

    ### Replace string in dataset
    reset session
    
    $Data <<EOD
    # data with special string separators
    2019-02-01|||123|||345|||567|||Some unpredictable textual data with pipes|,
    2019-02-02|||234|||345|||456|||weird symbols @ and commas, and so on.
    2019-02-03|||345|||234|||123|||text text text
    EOD
    
    # replace string function
    # prefix RS_ to avoid variable name conflicts
    replaceStr(s,s1,s2) = (RS_s='', RS_n=1, (sum[RS_i=1:strlen(s)] \
        ((s[RS_n:RS_n+strlen(s1)-1] eq s1 ? (RS_s=RS_s.s2, RS_n=RS_n+strlen(s1)) : \
        (RS_s=RS_s.s[RS_n:RS_n], RS_n=RS_n+1)), 0)), RS_s)
    
    set print $DataOutput
    do for [RS_j=1:|$Data|] {
        print replaceStr($Data[RS_j],"|||","\t")
    }
    set print
    
    print $DataOutput
    ### end of code
    

    哦,哇!我想这是对gnuplot的行家们来说的吧?您已经重新实现了
    sed's/| | |/\t/g'
    。。。只有30行gnuplot代码!那是。。。令人印象深刻的8-)虽然我不认为它遵循unix的原理(我想在这里使用
    sed
    会更惯用),但它令人敬畏,所以(+1)。
    RSID\u replaceStr
    -它是否在
    s1
    长度的所有片段上迭代,从增加
    RSID\u n
    开始,然后将其与
    s1
    进行比较,这大致正确吗?谢谢。我可以将代码简化为约10行;-)。是的,我是“mis”,使用表达式
    sum[I=1:N](,0)
    作为迭代器扫描字符串,并在需要时将
    s1
    替换为
    s2
    # data with special string separators
    2019-02-01  123 345 567 Some unpredictable textual data with pipes|,
    2019-02-02  234 345 456 weird symbols @ and commas, and so on.
    2019-02-03  345 234 123 text text text