R友好的希腊字符

时间:2019-04-05 09:08:59

标签: r character-encoding

我注意到我可以使用一些希腊字母作为名称,而另一些则是非法的,或者只是拉丁字母的别名。

基本上,我可以使用βµ(尽管在打印时β更改为ß,并且ßβ用作同盟广告)

list(β = 1)
# $ß
# [1] 1
list(μ = 1)
# $µ
# [1] 1

允许使用α,Γ,δ,ε,Θ,π,Σ,σ,τ,Φ,φ和Ω,但它们充当拉丁字母的同义字母。

list(α = 1)
# $a
# [1] 1

αa <- 42
aa
# [1] 42

GG <- 33
ΓΓ 
# [1] 33

我测试过的其他字母都不起作用:

ι <- 1
# Error: unexpected input in "\"
Λ <- 1
# Error: unexpected input in "\"
λ <- 1
#Error: unexpected input in "\"

我对λ感到惊讶,因为它由软件包wrapr的{​​{1}}定义,所以我认为这取决于系统。

我知道外观相似或相同的字符可能具有不同的编码,并且其中某些字符不能很好地在应用程序之间进行复制/粘贴,此问题的代码在粘贴回RStudio时会返回描述的输出。

?make.names说:

  

在语法上有效的名称由字母,数字和点或   带下划线的字符,并以字母或点开头   用数字

问题的一部分是:什么是字母?那这里发生了什么?

更具体地说:

  • 是否存在可以在所有R安装上安全使用的希腊字符,尤其是define_lambdaµ(或β)可以安全地在软件包中使用。
  • 为什么ß用户经常使用λintToUtf8(955))在我的系统上不可用。
  • 我是否可以安全地在代码中使用其他非拉丁字母(无论是否是希腊字母)? (例如挪威语wrapr看起来很酷,并且似乎可以在我的系统上运行)

这一切是由于我正在寻找一个(或2个)字符函数名称而不会与现有名称或常用名称冲突,并且看起来有些时髦。 ø已经被大量使用,我也已经使用..

来自.

sessionInfo()

1 个答案:

答案 0 :(得分:12)

无论如何我都不是专家,但让我们尝试分析问题。最后,编译器需要理解您的R代码,因此make.names()的源代码可能会有所帮助:

names <- as.character(names)
names2 <- .Internal(make.names(names, allow_))
if (unique) {
  o <- order(names != names2)
  names2[o] <- make.unique(names2[o])
}
names2

现在,.Internal()调用R解释器(用C编写),因此我们需要更深入一些。可以在以下位置找到负责处理make.names()请求的C代码:https://github.com/wch/r-source/blob/0dccb93e114b00b2fcbe75e8721f11a8f2ffdff4/src/main/character.c

一小段:

SEXP attribute_hidden do_makenames(SEXP call, SEXP op, SEXP args, SEXP env)
{
    SEXP arg, ans;
    R_xlen_t i, n;
    int l, allow_;
    char *p, *tmp = NULL, *cbuf;
    const char *This;
    Rboolean need_prefix;
    const void *vmax;

    checkArity(op ,args);
    arg = CAR(args);
    if (!isString(arg))
    error(_("non-character names"));
    n = XLENGTH(arg);
    allow_ = asLogical(CADR(args));
    if (allow_ == NA_LOGICAL)
    error(_("invalid '%s' value"), "allow_");
    PROTECT(ans = allocVector(STRSXP, n));
    vmax = vmaxget();
    for (i = 0 ; i < n ; i++) {
    This = translateChar(STRING_ELT(arg, i));
    l = (int) strlen(This);
    /* need to prefix names not beginning with alpha or ., as
       well as . followed by a number */
    need_prefix = FALSE;
    if (mbcslocale && This[0]) {
        int nc = l, used;
        wchar_t wc;
        mbstate_t mb_st;
        const char *pp = This;
        mbs_init(&mb_st);
        used = (int) Mbrtowc(&wc, pp, MB_CUR_MAX, &mb_st);
        pp += used; nc -= used;
        if (wc == L'.') {
        if (nc > 0) {
            Mbrtowc(&wc, pp, MB_CUR_MAX, &mb_st);
            if (iswdigit(wc))  need_prefix = TRUE;
        }
        } else if (!iswalpha(wc)) need_prefix = TRUE;
    } else {
        if (This[0] == '.') {
        if (l >= 1 && isdigit(0xff & (int) This[1])) need_prefix = TRUE;
        } else if (!isalpha(0xff & (int) This[0])) need_prefix = TRUE;
    }
    if (need_prefix) {
        tmp = Calloc(l+2, char);
        strcpy(tmp, "X");
        strcat(tmp, translateChar(STRING_ELT(arg, i)));
    } else {
        tmp = Calloc(l+1, char);
        strcpy(tmp, translateChar(STRING_ELT(arg, i)));
    }
    if (mbcslocale) {
        /* This cannot lengthen the string, so safe to overwrite it. */
        int nc = (int) mbstowcs(NULL, tmp, 0);
        if (nc >= 0) {
        wchar_t *wstr = Calloc(nc+1, wchar_t);
        mbstowcs(wstr, tmp, nc+1);
        for (wchar_t * wc = wstr; *wc; wc++) {
            if (*wc == L'.' || (allow_ && *wc == L'_'))
            /* leave alone */;
            else if (!iswalnum((int)*wc)) *wc = L'.';
        }
        wcstombs(tmp, wstr, strlen(tmp)+1);
        Free(wstr);
        } else error(_("invalid multibyte string %d"), i+1);
    } else {
        for (p = tmp; *p; p++) {
        if (*p == '.' || (allow_ && *p == '_')) /* leave alone */;
        else if (!isalnum(0xff & (int)*p)) *p = '.';
        /* else leave alone */
        }
    }
//  l = (int) strlen(tmp);        /* needed? */
    SET_STRING_ELT(ans, i, mkChar(tmp));
    /* do we have a reserved word?  If so the name is invalid */
    if (!isValidName(tmp)) {
        /* FIXME: could use R_Realloc instead */
        cbuf = CallocCharBuf(strlen(tmp) + 1);
        strcpy(cbuf, tmp);
        strcat(cbuf, ".");
        SET_STRING_ELT(ans, i, mkChar(cbuf));
        Free(cbuf);
    }
    Free(tmp);
    vmaxset(vmax);
    }
    UNPROTECT(1);
    return ans;
}

我们可以看到,使用了诸如wchar_t(http://icu-project.org/docs/papers/unicode_wchar_t.html)之类的依赖于编译器的数据类型。这意味着make.names()的行为取决于用于编译R解释器本身的C编译器。问题在于C编译器不是很标准化,因此无法假设字符的行为。包括操作系统,硬件,语言环境等在内的所有内容都可以更改此行为。

最后,如果要保存,我会坚持使用ASCII字符,尤其是在不同操作系统之间共享代码时。