4

最初の質問。文字列の最後の文字がマルチバイトでないかどうかを Lua で判断する最も簡単な方法は何ですか? または、文字列から最後の文字を削除する最も簡単な方法は何ですか?

有効な文字列の例と、関数の出力を希望するものを次に示します

hello there     --- result should be:   hello ther
anñ             --- result should be:   an
כראע            --- result should be:   כרא
ㅎㄹㅇㅇㅅ       --- result should be:   ㅎㄹㅇㅇ

次のようなものが必要です

function lastCharacter(string)
    --- some code which will extract the last character only ---
    return lastChar
end

またはそれがより簡単な場合

function deleteLastCharacter(string)
--- some code which will output the string minus the last character --- 
    return newString
end

これが私が進んできた道です

local function lastChar(string)
    local stringLength = string.len(string)
    local lastc = string.sub(string,stringLength,stringLength)
    if lastc is a multibyte character then
        local wordTable = {}
        for word in string:gmatch("[\33-\127\192-\255]+[\128-\191]*") do
            wordTable[#wordTable+1] = word
        end
    lastc = wordTable[#wordTable]
end
    return lastc
end
4

3 に答える 3

9

まず第一に、Lua のstringライブラリには Unicode/マルチバイト エンコーディングについて何も知らない関数がないことに注意してください (ソース: Programming in Lua, 3rd edition)。Lua に関する限り、文字列は単純にバイトで構成されています。UTF-8 でエンコードされた文字列を使用している場合、どのバイトが文字を構成するかを判断するのはあなた次第です。したがって、文字数ではなく、バイトstring.len数が得られます。そして、文字の部分文字列ではなく、バイトの部分文字列を提供します。string.sub

UTF-8 の基本:

Unicode の基本的な概念を一新する必要がある場合は、この記事をチェックしてください。

UTF-8 は Unicode の可能な (そして非常に重要な) 実装の 1 つであり、おそらくあなたが扱っているものです。UTF-32 および UTF-16 とは対照的に、各文字をエンコードするために可変バイト数 (1 から 4) を使用します。特に、ASCII 文字 0 ~ 127 は 1 バイトで表されるため、UTF-8 を使用して ASCII 文字列を正しく解釈できます (これらの 128 文字のみを使用する場合はその逆も同様です)。他のすべての文字は、194 ~ 244 の範囲のバイトで始まります (これは、完全な文字をエンコードするためにさらにバイトが続くことを示します)。この範囲はさらに細分化されているため、このバイトからさらに 1、2、または 3 バイトが続くかどうかがわかります。これらの追加バイトは継続バイトと呼ばれ、128 ~ 191 の範囲からのみ取得されることが保証されています。したがって、

  • にある場合は[0,127]、1 バイト (ASCII) 文字です。
  • にある場合[128,191]、それはより長い文字の一部であり、それ自体では意味がありません
  • にある場合[191,244]は、長い文字の始まりを示します (そして、その文字の長さを教えてくれます)。

この情報は、文字数を数えたり、UTF-8 文字列を文字に分割したり、その他のあらゆる種類の UTF-8 に依存する操作を行うのに十分です。

パターンマッチングの基本:

当面のタスクには、Lua のパターン マッチング構造がいくつか必要です。

[...]クラス内の文字の 1 文字 (またはむしろbyte ) に一致する文字クラスです。たとえば、 、 または のいずれかに[abc]一致します。ハイフンを使用して範囲を定義できます。したがって、たとえば、 は からまでのいずれかのバイトに一致します。これは、Lua 文字列 (パターンだけでなく)で使用できるエスケープ シーケンスであり、対応する ASCII 文字の代わりに数値でバイトを指定できることに注意してください。たとえば、は と同じです。abc[\33-\127]33127\127"a""\97"

(クラスの一部ではない^任意の 1 バイトに一致するように、文字クラスを () で開始することにより、文字クラスを無効にすることができます。

*前のトークンを 0 回以上 (任意の回数 - できるだけ頻繁に) 繰り返します。

$アンカーです。パターンの最後の文字の場合、パターンは文字列の最後でのみ一致します。

そのすべてを組み合わせて...

...あなたの問題はワンライナーになります:

local function lastChar(s)
    return string.match(s, "[^\128-\191][\128-\191]*$")
end

これは、UTF-8 継続文字ではない文字 (つまり、1 バイト文字か、より長い文字の開始を示すバイト) に一致します。次に、任意の数の継続文字 (選択された範囲により、現在の文字を超えることはできません) に一致し、その後に文字列の末尾 ( $) が続きます。したがって、これにより、文字列の最後の文字を構成するすべてのバイトが得られます。4 つの例すべてで目的の出力が生成されます。

gsub同様に、文字列から最後の文字を削除するために使用できます。

function deleteLastCharacter(s)
    return string.gsub(s, "[^\128-\191][\128-\191]*$", "")
end

一致は同じですが、一致した部分文字列を返す代わりに、それを置き換えて""(つまり、削除して)、変更された文字列を返します。

于 2013-04-12T20:56:17.990 に答える
3

ここでプラピンのソリューションを使用します:

function lastCharacter(str)
  return str:match("[%z\1-\127\194-\244][\128-\191]*$")
end

次に、戻り値の長さを取得して、それがマルチバイトかどうかを確認できます。gsub関数を使用して文字列から削除することもできます。

function deleteLastCharacter(str)
  -- make sure to add "()" around gsub to force it to return only one value
  return(str:gsub("[%z\1-\127\194-\244][\128-\191]*$", ""))
end

for _, str in pairs{"hello there", "anñ", "כראע"} do
  print(str, " -->-- ", deleteLastCharacter(str))
end

これらのパターンは、有効な UTF-8 文字列でのみ機能することに注意してください。無効の可能性があるものがある場合は、より複雑なロジックを適用する必要がある場合があります。

于 2013-04-12T21:14:04.817 に答える