web-dev-qa-db-ja.com

バッチファイルでforループなしで文字列を分割する方法

Forループを使用せずに、文字列を2つの部分に分割したい。

たとえば、変数に文字列があります。

str=45:abc

私は手に入れたい 45を変数に、abcを別の変数に。バッチファイルで可能ですか?

パターンはsomenumber:somestringのようなものです

14
CrazyCoder

Strをさまざまな方法で分割できます。

Forループは使用しないでください。

末尾の部分は*を使用すると簡単です(...までは何でも一致)
set "var2=%str:*:=%"

先頭部分は厄介なトリックで行うことができます
set "var1=%str::="^&REM #%

アンパサンドをエスケープするには、キャレットが必要です。
事実上、コロンは"&REM #に置き換えられますので、あなたの場合、置き換えた後に行を得ました
set "var1=4567"&REM #abcde
そして、これは2つのコマンドに分割されます

set "var1=4567"
REM #abcde` 

そして完全なコードはここにあります:

set "str=4567:abcde"
echo %str%
set "var1=%str::="^&REM #%
set "var2=%str:*:=%"
echo var1=%var1% var2=%var2%

編集2:より安定したリーディングパート

改行を使用するアイデアをDaveに感謝します。
REMテクニックは、引用符と特殊文字を含むコンテンツに対してあまり安定していません。
しかし、改行のトリックでは、split引数が単一の文字より長い場合にも機能する、より安定したバージョンが存在します。

@echo off
setlocal enableDelayedExpansion
set ^"str=456789#$#abc"
for /F "delims=" %%a in (^"!str:#$#^=^

!^") do (
  set "lead=%%a"
  goto :break
)
:break
echo !lead!

ソリューション3:Adpated dbenhamsの回答

Dbenhamは彼のソリューションでパイプ付きのラインフィードを使用しています。
これは少し複雑すぎるようです。
ソリューションがファクトを使用するので、パーサーはエスケープされていない改行の後に残りの行を削除します(これが 特殊文字フェーズ の前または中にある場合)。

最初に、コロン文字は遅延拡張置換で改行に置き換えられます。
それが許可され、改行が変数の一部になりました。
次に、行set lead=%lead%が末尾の部分を削除します。
引用符が文字列の一部である場合、set "lead=%lead%"は機能しなくなるため、ここでは拡張構文を使用しないことをお勧めします。

setlocal enableDelayedExpansion
set "str=45:abc"
set ^"lead=!str::=^

!"
set lead=%lead%
echo "!lead!"
17
jeb

あなたはこれを試すことができます。固定されている場合、コロンの左側の数字は常に2で、右側の数字は3になります。その後、次のコードは、strに値があると想定して機能します。

set "str=45:abc"
echo %str%
set var1=%str:~0,2%
set var2=%str:~3,3%
echo %var1% %var2%

投稿してください。 :)

はい、私はこれが非常に古いトピックであることを知っていますが、私はそれを発見したばかりであり、私の解決策を投稿する誘惑に抵抗することはできません。

@echo off
setlocal

set "str=45:abc"
set "var1=%str::=" & set "var2=%"
echo var1="%var1%"  var2="%var2%"

あなたはこの方法の完全な詳細 ここ を読むことができます。

3
Aacini

FORループの使用を避けるのは無意味に思われますが、問題が面白くなります。

Jebが指摘したように、!str:*:=!を使用すると、末尾部分を簡単に取得できます。

トリッキーなビットが主役です。ここにジェブの解決策の代替案があります。

次の構文を使用して、:の代わりに変数にラインフィードを挿入できます

setlocal enableDelayedExpansion
set "str=45:abc"
echo !str::=^

!

- 出力 -

45
abc

最後の!の上にある空の行は重要です。

理由はわかりませんが、上記の出力をコマンドにパイプすると、最初の行だけが保持されます。したがって、出力を任意の行に一致するFINDSTRにパイプ処理し、その結果をファイルに送信して、SET/Pを使用して変数に読み込むことができます。

SET/Pは<LF>を行末記号として認識しないため、2行目はSET/Pを使用する前に削除する必要があります-<CR><LF>のみを認識します。

ここに完全なソリューションがあります:

@echo off
setlocal enableDelayedExpansion
set "str=45:abc"
echo(!str::=^

!|findstr "^" >test.tmp
<test.tmp set /p "var1="
del test.tmp
set "var2=!str:*:=!"
echo var1=!var1!  var2=!var2!

更新

私は、2行目が出力から削除される理由をほとんど理解したと思います:)

これは、パイプがWindowsのcmd.exeによってどのように処理され、各サイドが新しいCMD.EXEスレッドによって処理されるかに関係しています。 jebからのすばらしい回答のある関連する質問については、 パイプのコードブロック内で遅延拡張が失敗するのはなぜですか? を参照してください。

パイプされたコマンドの左側を見るだけで、(メモリ内で)次のようなステートメントに解析されると思います

C:\Windows\system32\cmd.exe /S /D /c" echo {delayedExpansionExpression}"

{delayedExpansionExpression}を使用して、複数行の検索を表し、まだ発生していない展開を置き換えます。

次に、変数式が実際に展開され、検索と置換によって行が2つに分割されていると思います。

C:\Windows\system32\cmd.exe /S /D /c" echo 43
abc"

その後にのみコマンドが実行され、通常のcmd.exeルールにより、コマンドは改行で終了します。引用符で囲まれたコマンド文字列には終了引用符がありませんが、パーサーはそれを気にしません。

私がまだ困惑している部分は、abc"に何が起こるのですか?それを実行しようとすると、 'abc "'のようなエラーメッセージが内部または外部コマンドとして認識されず、動作可能なプログラムまたはバッチファイル。代わりに、エーテルで単純に失われるように見えます。

注-jebの3番目のコメントで理由を説明します:)


FORなしの安全なバージョン

私の元の解決策は、this & that:cats & dogsのような文字列では機能しません。これは、文字列の長さの制限と後続の制御文字が先頭部分から削除されることを除いて、ほとんどすべての文字列で機能するFORなしのバリエーションです。

@echo off
setlocal enableDelayedExpansion
set "str=this & that:cats & dogs"
set ^"str2=!str::=^

!^"
cmd /v:on /c echo ^^!str2^^!|findstr /v "$" >test.tmp
<test.tmp set /p "var1="
del test.tmp
set "var2=!str:*:=!"
echo var1=!var1!  var2=!var2!

新しいCMDスレッドまで展開を遅らせ、$<cr>で終わる行にのみ一致するというFINDSTR正規表現の癖を使用します。 1行目にはありませんが、2行目にはあります。 /vオプションは結果を反転します。

3
dbenham

これは、リーディングピースの厄介なトリックのない解決策です

    REM accepts userID@Host
    setlocal enableDelayedExpansion
    set "str=%1"
    set "Host=%str:*@=%"
    for /F "tokens=1 delims=@" %%F IN ("%str%") do set "user=%%F"
    echo user@Host = %user%@%Host%
    endlocal
0
bravomail

ここで変数を分割するためのあらゆる種類のメトスを投稿する人々の光の中で、私は自分のメソッドを投稿することもできますoneだけでなくseveralも変数から分割し、同じ記号で示されていますが、これはREM-Methodでは使用できません(@jebを使用してしばらくの間使用していました)。

以下の方法では、2行目で定義された文字列が3つの部分に分割されます。

setlocal EnableDelayedExpansion
set fulline=one/two/three or/more
set fulline=%fulline%//
REM above line prevents unexpected results when input string has less than two /

set line2=%fulline:*/=%
set line3=%line2:*/=%

set line1=!fulline:/%line2%=!
set line2=!line2:/%line3%=!

setlocal DisableDelayedExpansion

echo."%line1%"
echo."%line2%"
echo."%line3%"

出力:

"one"
"two"
"three or/more//"

最後に作成した文字列のパーティションを、残りの「安全」な分割文字の「ビン」として使用することをお勧めします。

0
hammockdude