3

str値を代入する変数があるとします(test\\ttestまたは、この場合、実際には だけである可能性があります\\)。私がやりたいのは、二重のバックスラッシュを単一のバックスラッシュに置き換えることです。

目的は明確です: エスケープ シーケンス (水平タブ) を出力したいのですが\t、今はプレーン テキストとして出力するだけです\t

私が使用できないことも明らかです:

str:gsub("\\","\")

構文エラーが発生\"し、エスケープ シーケンスとして認識されるためです。私は出てくることができるすべての方法で試しました。また、loadstring() (およびネストされた loadstring() 呼び出しも) を使用してみましたが、失敗しました。

やれと言わないでください:

str:gsub("\\t","\t")

もちろん、それは機能しますが、私が必要としているものではありません。二重のバックスラッシュを単一のバックスラッシュに置き換える必要があります。

4

1 に答える 1

5

string.gsubバックスラッシュ文字を置き換えることができるため、引用によって混乱していると思われます。

C:...> ルア
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org、PUC-Rio
> a="test\\\\ttest"
> =a
テスト\\tテスト
> =a:gsub([[\\]],[[\]])
テスト\tテスト 1
>

[[...]]バックスラッシュは、二重引用符および単一引用符で囲まれた文字列で文字エスケープとして使用されますが、表記法で記述された長い文字列では使用されません。通常の文字列定数では、バックスラッシュは後続の 1 つ以上の文字を消費し、シーケンス全体を内部文字列値の 1 バイトに置き換えます。バックスラッシュ"\\"を 1 つ含む単一バイト文字列"\"、構文エラー、および"\""二重引用符を含む単一バイト文字列も同様です。

さらに混乱を招くのは、Lua パターンstring.gsub(およびその兄弟) が理解するよう%に、特殊なパターンの引用と名前付けに文字を使用することです。これは、Lua のパターンと、他の言語でサポートされている正規表現との明らかな違いの 1 つです。Lua パターンにとって、バックスラッシュはただの普通の文字です。

したがって、a上記の値を設定するときに、追加のバックスラッシュを使用して、文字列値が合計 2 つになるようにしました。a=[[test\\ttest]]私は同じ趣旨で書くことができたでしょう。への呼び出しは、2 つgsubのバックスラッシュを 1 つに置き換えた単純なパターンで記述されています。ご覧のとおり、成功し、結果は文字列ですtest\ttest(2 番目の戻り値として一致数が返されます)。

要するに、質問で求めた置換は、期待どおりに「機能する」だけです。

しかし、行間を読むと、それはあなたが望んでいたものではありません。test\\ttest文字列をに変換しようとしているようtest<TAB>testです。その単一の変換が必要な場合は、次のように記述してください a:gsub([[\\t]],"\t")。(文字列リテラル\t置換値の ASCII 文字を意味すると解釈するように、引用符を使用したことに注意してください。)

タブ、ベル、バックスペース、キャリッジ リターン、改行などの通常の 1 文字エスケープを処理する必要があるだけでなく、1 ~ 3 桁の 10 進数コードも処理する必要があるため、より一般的なケースはより困難です。順序。

更新: Lua コンパイラが文字列リテラルに対して行うように、すべてのバックスラッシュ エスケープを処理する何かを書きたいという誘惑が強すぎることが判明しました。

function unbackslashed(s)
    local ch = {
        ["\\a"] = '\\007', --'\a' alarm             Ctrl+G BEL
        ["\\b"] = '\\008', --'\b' backspace         Ctrl+H BS
        ["\\f"] = '\\012', --'\f' formfeed          Ctrl+L FF
        ["\\n"] = '\\010', --'\n' newline           Ctrl+J LF
        ["\\r"] = '\\013', --'\r' carriage return   Ctrl+M CR
        ["\\t"] = '\\009', --'\t' horizontal tab    Ctrl+I HT
        ["\\v"] = '\\011', --'\v' vertical tab      Ctrl+K VT
        ["\\\n"] = '\\010',--     newline
        ["\\\\"] = '\\092',--     backslash
        ["\\'"] = '\\039', --     apostrophe
        ['\\"'] = '\\034', --     quote
    }
    return s:gsub("(\\.)", ch)
        :gsub("\\(%d%d?%d?)", function(n)
            return string.char(tonumber(n))
        end)
end

このような関数は、ユーザーが提供したテキストを解析し、ユーザーが提供したテキスト内のバックスラッシュ エスケープを処理したい場合に役立ちます。文字列リテラルは、コンパイラによって既に処理されている必要があります。

もう 1 つの注意点は、文字列が部分的に翻訳されていることに気付いた場合、実際にはデザインがわかりにくくなっている可能性があるということです。ユーザー入力の解析以外でこのような関数が実際に必要な場合は、設計に深刻な問題がある可能性があることを示しています。

この関数unbackslashedは、バックスラッシュとそれに続く 1 文字の形式をとるすべての認識されたシーケンスを、最初に同等の数値形式に置き換えることによって機能します。2 番目のパスでは、すべての数値形式をそのリテラル文字に変換します。によって認識される文字列パターンstring.gsubは、完全な正規表現パーサーでサポートされている代替表記法をサポートしていないため、2 つのパスが必要でした。それ以外の場合は、一致するパターンを Perl のものと同様に記述/\\([0-9]{1-3})|\\(.)/し、置換を 1 回のパスで実行できた可能性があります。

于 2013-11-13T19:48:00.927 に答える